代码分享的前 GitHub 时代:Planet Source Code 的兴衰与启示

2026-05-12 24 预计阅读时间:1 分钟
来源:oschina.net AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:12 分钟

Chris Pietschmann 最近在 GitHub 上翻到了一个奇怪的存档页面——那是 Planet Source Code(PSC)的代码索引,上面赫然列着他在 2002 到 2004 年间提交的几十段 VBScript、ASP 和 JavaScript 代码。二十多年前的自己突然跳到眼前,他写了一篇怀旧长文,引发了一波老开发者的集体回忆。

这不仅仅是一段个人回忆。PSC 的兴衰折射出代码分享方式的根本变迁:从"贴代码"到"推仓库",从单文件到项目,从浏览下载到协作迭代。

Planet Source Code 是什么

PSC 在 1999 年左右上线,定位非常直白——一个代码片段的发布与浏览站点。开发者把一段自认为有用的代码贴上去,附上说明和分类标签,其他人搜索、下载、评分。站点按语言分区:VB、Java、C/C++、ASP、JavaScript……每个分区还有"周最佳代码"评选。

在那个年代,这几乎是普通开发者发布代码的唯一可行渠道。个人网站 Hosting 不便宜,FTP 空间有限,SourceForge 只接纳正式的开源项目。一段 50 行的 VBScript 工具函数?没地方放。PSC 填的就是这个空档。

它的交互模式可以概括为:

  • 发布者:把代码复制粘贴到网页表单,选语言、写描述、提交
  • 浏览者:按语言/关键词搜索,看到代码后复制到本地,给个评分
  • 反馈:评论区留言,没有 diff、没有 fork、没有 issue

这套模式在今天看来笨拙,但在 2000 年前后,它就是最接近"开源社区"的东西。

前 GitHub 时代的代码分享生态

PSC 并不是孤例。那个年代有一整套代码分享基础设施,各有侧重:

平台 定位 交互方式
Planet Source Code 代码片段发布与评分 粘贴提交、复制下载、评论
SourceForge 正式开源项目托管 CVS/SVN、下载 tarball、邮件列表
CodeProject 技术文章+附带代码 长文教程、代码下载、论坛讨论
Snipplr / dzone snippets 小片段收藏 粘贴、标签、收藏
个人网站/FTP 自由发布 手动上传、链接分享
Usenet / 邮件列表 讨论中附带代码 纯文本贴代码、手动复制

这些平台有一个共同特征:代码是静态的发布物,不是活的协作对象。你下载一段代码后,改了什么、修了哪个 bug,原作者完全不知道。改进版本要么重新发布一个新条目,要么就淹没在评论区的某条留言里。

从"贴一段代码"到"推一个仓库"到底改变了什么

GitHub 带来的不只是 git 托管。它改变了代码分享的几个根本假设:

1. 代码从"成品"变成"过程"

PSC 上你看到的是一段最终代码。GitHub 上你看到的是 commit 历史——每一步尝试、每一个回退、每一次重构。代码变成了有时间维度的活文档。

2. 改进从"重新发布"变成"fork + PR"

在 PSC,如果你改进了某人的代码,你要么自己发一个新条目,要么在评论区贴修改版。两条代码之间没有结构化的关联。GitHub 的 fork/PR 让改进直接回流到源头。

3. 发现从"分类浏览"变成"网络效应"

PSC 的发现机制是人工分类和评分排序。GitHub 的发现靠社交网络——你看到某人 star 了一个项目,依赖链把相关项目推到你面前,趋势算法放大活跃项目。

4. 代码从"片段"变成"项目上下文"

PSC 的代码是剥离了环境的纯片段。GitHub 上哪怕是一个小工具,也有 README、依赖声明、构建脚本、测试。你拿到的不只是一段逻辑,而是可运行的项目。

实践:给自己的旧代码建一个本地"时光胶囊"

PSC 的存档之所以能引发共鸣,是因为很多开发者手里都有散落各处的旧代码——硬盘里的 .vbs 文件、旧邮件附件里的脚本、甚至纸质打印的源码。与其等某个平台偶然存档,不如自己动手建一个本地索引。

下面是一个 Python 脚本,扫描指定目录下的源码文件,生成一个静态 HTML 紓览页面——本质上就是一个小型 Planet Source Code,只不过数据在你自己手里:

#!/usr/bin/env python3
"""codeshelf.py — 把散落的旧代码文件整理成一个可浏览的本地 HTML 紓引"""

import os
import sys
import hashlib
from datetime import datetime
from pathlib import Path

# 支持的源码扩展名及对应语言标签
LANG_MAP = {
    ".vbs": "VBScript", ".bas": "VB6", ".cls": "VB6",
    ".js": "JavaScript", ".html": "HTML",
    ".asp": "ASP", ".php": "PHP",
    ".py": "Python", ".pl": "Perl",
    ".c": "C", ".h": "C Header", ".cpp": "C++",
    ".java": "Java", ".sh": "Shell",
    ".sql": "SQL", ".ps1": "PowerShell",
}

HTML_TEMPLATE = """<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<title>Code Shelf — {title}</title>
<style>
  body {{ font-family: monospace; max-width: 960px; margin: 2em auto; background: #1e1e1e; color: #d4d4d4; }}
  h1 {{ color: #569cd6; }}
  h2 {{ color: #dcdcaa; margin-top: 2em; }}
  .meta {{ color: #608b4e; font-size: 0.85em; }}
  pre {{ background: #252526; padding: 1em; border-radius: 4px; overflow-x: auto; }}
  .lang-tag {{ background: #264f78; padding: 2px 8px; border-radius: 3px; font-size: 0.8em; }}
  a {{ color: #4ec9b0; }}
</style>
</head>
<body>
<h1>📦 Code Shelf — {title}</h1>
<p class="meta">生成时间: {timestamp} | 文件数: {count}</p>
{entries}
</body>
</html>"""

ENTRY_TEMPLATE = """
<h2>{filename} <span class="lang-tag">{lang}</span></h2>
<p class="meta">路径: {relpath} | 大小: {size} bytes | SHA256: {hash}</p>
<pre><code>{content}</code></pre>
"""

def sha256_of(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8", errors="replace")).hexdigest()[:16]

def build_shelf(scan_dir: str, output_file: str = "codeshelf.html"):
    scan_path = Path(scan_dir).resolve()
    entries_html = []
    count = 0

    for filepath in sorted(scan_path.rglob("*")):
        if filepath.suffix.lower() not in LANG_MAP:
            continue
        if filepath.is_dir() or filepath.stat().st_size > 100_000:
            # 跳过目录和超过 100KB 的文件
            continue

        try:
            content = filepath.read_text(encoding="utf-8", errors="replace")
        except Exception:
            content = filepath.read_text(encoding="latin-1", errors="replace")

        relpath = filepath.relative_to(scan_path)
        lang = LANG_MAP.get(filepath.suffix.lower(), "Unknown")
        entries_html.append(ENTRY_TEMPLATE.format(
            filename=filepath.name,
            lang=lang,
            relpath=relpath,
            size=filepath.stat().st_size,
            hash=sha256_of(content),
            content=content,
        ))
        count += 1

    full_html = HTML_TEMPLATE.format(
        title=scan_path.name,
        timestamp=datetime.now().strftime("%Y-%m-%d %H:%M"),
        count=count,
        entries="\n".join(entries_html),
    )

    out = Path(output_file)
    out.write_text(full_html, encoding="utf-8")
    print(f"✅ 已生成 {out},收录 {count} 个文件")
    print(f"   用浏览器打开: file://{out.resolve()}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("用法: python codeshelf.py <扫描目录> [输出文件名]")
        print("示例: python codeshelf.py ./my_old_code ./shelf.html")
        sys.exit(1)
    build_shelf(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else "codeshelf.html")

运行方式:

# 把你散落各处的旧代码复制到一个目录
mkdir -p ~/my_old_code
cp ~/archive/2002/*.vbs ~/my_old_code/
cp ~/archive/2003/*.asp ~/my_old_code/
cp ~/old_scripts/*.js  ~/my_old_code/

# 生成浏览页面
python codeshelf.py ~/my_old_code ~/shelf.html

# 打开查看
open ~/shelf.html   # macOS
# 或 xdg-open ~/shelf.html  # Linux

生成的页面按文件名排列,每个文件标注语言、路径、大小和 SHA256 摘要。你可以随时追加新文件再重新生成——比 PSC 的粘贴提交更可控,数据也完全在你手里。

如果想更进一步,可以把 shelf.html 和源码目录一起推到一个 GitHub 仓库,加上 README 说明年代和背景,这就是一个自建的代码存档。

我们失去了什么,又该保留什么

PSC 的消亡不是技术落后那么简单。它身上有一些 GitHub 没有完全继承的东西:

低门槛的片段分享。GitHub 的最小单元是仓库,哪怕一个单文件工具也要有仓库结构。PSC 让你贴一段 30 行的函数就能发布,这个门槛差对初学者和随手分享意义重大。今天 GitHub Gist 部分填补了这个空档,但 Gist 的发现机制远不如 PSC 的分类浏览和评分排序。

人工评选的质量信号。PSC 的"周最佳代码"是人工评选的,带有编辑判断。GitHub 的 Trending 是算法驱动的,偏向已有关注度的项目。两种信号各有盲区,但人工评选能挖掘冷门但高质量的贡献,这是算法容易漏掉的。

面向学习的代码浏览。PSC 的核心体验是"逛"——按语言翻、看评分、读代码、学技巧。GitHub 的核心体验是"用"——找到项目、clone、装依赖、跑起来。前者是学习姿态,后者是工程姿态。两者都需要,但今天的平台明显偏工程。

如果你手里有值得保留的旧代码,别等平台偶然存档。用上面的脚本或类似方法,自己建一个本地索引,再推到 GitHub 做长期保存。二十年后翻出来,你会感谢今天的自己。


相关推荐