原文:Go’s Version Control History
引用:Why Google Stores Billions of Lines of Code in a Single Repository
A TUTORIAL INTRODUCTION TO THE LANGUAGE B
open(2) — Linux manual page
THE PROGRAMMING LANGUAGE B
当我们使用 git clone https://github.com/golang/go.git 拉取Go的源码后,想必有不少人会和我一样拉到git history的最后,想追寻一下Go的起点,那我们会看到
这就是Go代码库的最早的四个提交,然而让人惊讶的是,最早的提交竟然在 1972年!!
那为什么最早的提交在1972年呢?
在Go作者之一的 Russ Cox 的博客中,第一段就回答了这个有趣的问题。
Every once in a while someone notices the first commit in the Go repo is dated 1972:
% git log --reverse --stat
commit 7d7c6a97f815e9279d08cfaea7d5efb5e90695a8
Author: Brian Kernighan <bwk>
AuthorDate: Tue Jul 18 19:05:45 1972 -0500
Commit: Brian Kernighan <bwk>
CommitDate: Tue Jul 18 19:05:45 1972 -0500
hello, world
R=ken
DELTA=7 (7 added, 0 deleted, 0 changed)
src/pkg/debug/macho/testdata/hello.b | 7 +++++++
1 file changed, 7 insertions(+)
...
bviously something silly is going on, and people usually stop there.
Cox 回答到,每隔一段时间就会有人来问这个问题,但是把重点放在这里,显然是一件傻事。
然而事实上,Go的真正起点是第五个提交。
这个提交只写了一个go_spec的文本文件,里面记录了一些最初的Go语法规则。
在之后被重命名为go_lang.txt
而此处有个有意思的点,SVN=111041,即该提交的svn版本号是111041,而提交时间也是2008年,难道说1972-2008年间还有111040个提交,只是没有显示出来?
现实很尴尬,Go起点确实就是2008年,并不是1972年,而且细看会发现,最早提交信息hello, world里是一个用B语言写的hello world。那到底是怎么回事呢?
Subversion
Go最开始使用的svn做的版本控制,代码放在一个小型的monorepo服务器上,这个服务器并不只有Go,还有其它项目,所以这个111041只是Go开始的版本号而以,而且之后的svn提交,其版本号也并不连续。
Perforce
svn并没有用多久,很快,在2008/7/22提交了最后一个svn提交
commit 777ee7163bba96f2c9b3dfe135d8ad4ab837c062
Author: Rob Pike <r@golang.org>
AuthorDate: Mon Jul 21 16:18:04 2008 -0700
Commit: Rob Pike <r@golang.org>
CommitDate: Mon Jul 21 16:18:04 2008 -0700
map delete
SVN=128258
doc/go_lang.txt | 6 ++++++
1 file changed, 6 insertions(+)
随后代码迁移到Perforce服务器,并做了第一个提交
commit 05caa7f82030327ccc9ae63a2b0121a029286501
Author: Rob Pike <r@golang.org>
AuthorDate: Mon Jul 21 17:10:49 2008 -0700
Commit: Rob Pike <r@golang.org>
CommitDate: Mon Jul 21 17:10:49 2008 -0700
help management of empty pkg and lib directories in perforce
R=gri
DELTA=4 (4 added, 0 deleted, 0 changed)
OCL=13328
CL=13328
lib/place-holder | 2 ++
pkg/place-holder | 2 ++
src/cmd/gc/mksys.bash | 0
3 files changed, 4 insertions(+)
从提交信息中可以看到新的标记,OCL源版本号,CL最终版本号,DELTA改动总数,R reviewer
由于经过代码审查后也可能会有一些更改,所以OCL通常表示最初提交的版本号,CL则表示最终版本号,而R则代表代码审核人,gri即Robert Griesemer,而大多数的提交OCL和CL都是相同的,即review后并没有修改。
谷歌是一个重度的Perforce使用者,而且很多代码都放在一个超大的monorepo服务器中,而Go代码不必在负载很高的服务器内,所以托管在辅助服务器上了。
Go 的大部分预开源开发都是在 Perforce 服务器上完成的。
Mercurial
直到2009年10月,Go的代码完成了最后一个Perforce提交,并迁移到了Mercurial
commit 942d6590d9005f89e971ed5af0374439a264a20e
Author: Kai Backman <kaib@golang.org>
AuthorDate: Fri Oct 23 11:03:16 2009 -0700
Commit: Kai Backman <kaib@golang.org>
CommitDate: Fri Oct 23 11:03:16 2009 -0700
one more argsize fix. we were copying with the correct
alignment but not enough (duh).
R=rsc
APPROVED=rsc
DELTA=16 (13 added, 0 deleted, 3 changed)
OCL=36020
CL=36024
src/cmd/5g/ggen.c | 2 +-
test/arm-pass.txt | 17 +++++++++++++++--
2 files changed, 16 insertions(+), 3 deletions(-)
也正是从Perforce迁移到Mercurial的时候Russ Cox引入了最初的这个hello, world提交。迁移的过程非常的繁琐,Subversion 和 Perforce 存储库都是 Google 内部的服务器,每个人都有“用户目录”,就像/usr/rsc在 repo 中一样。这些目录包含各种未发布代码,无论是因为它是 Google 内部技术,还是因为它根本不值得发布。
Cox花了大约一周的时间,而且非常困难。因为如果从某些提交中删除了一个文件,但随后它在其他提交中被重命名,Mercurial 会提醒重命名了未创建的文件。如果在创建文件时添加了版权声明,则必须小心以后的提交,以免在更改文件头部时出现合并冲突。
由于Cox将 repo 的提交拆分为文件形式,因此很容易创建一些虚假提交,这些提交作为节彩蛋,至少显示了该程序的真实历史的一部分。
hello, world
1972 年 7 月的提交展示了第一个“hello, world”程序,复制自 Brian Kernighan 的“A Tutorial Introduction to the Language B”(链接见引用 )
main( ) {
extrn a, b, c;
putchar(a); putchar(b); putchar(c); putchar(’!*n’);
}
a ’hell’;
b ’o, w’;
c ’orld’;
1974 年 1 月的提交 convert to C,如 Kernighan 的“ Programming in C — A Tutorial ”中所述(链接见引用)。链接的 PDF 是 Dennis Ritchie 重新输入的副本,没有日期,但 Ritchie于 1974 年 1 月 15 日的“C Reference Manual”技术备忘录将 Kernighan 的教程引用为“Unpublished internal memorandum, Bell Laboratories, 1974”,暗示该教程必须也是一月份写的。那个 C 程序比我们今天习惯的要短,但比 B 程序好得多:
main( ) {
printf("hello, world");
}
Cox并没有完全使用 The C Programming Language 第一版中的试例,原书中的试例应该是这样的:
main() {
printf("hello, world\n");
}
1988年4 月的提交了来自The C Programming Language 第二版的“Draft-Proposed ANSI C”版本的程序:
#include <stdio.h>
main()
{
printf("hello, world\n");
}
1988 年 4 月的第二次提交 显示了我们今天所知道的最终完整的 ANSI C 版本:
#include <stdio.h>
int
main(void)
{
printf("hello, world\n");
return 0;
}
而这也就是Go最初的四个提交的来历。
Public Mercurial
2009 年 11 月 10 日 Google Code Mercurial 存储库正式发布,为了纪念这一时刻,Go项目组又加了一个彩蛋。
Brian Kernighan 和 Rob Pike 1984 年出版的《Unix 编程环境》一书中的脚注中写到:
Ken Thompson 曾经被问到,如果让他来重新设计UNIX系统,他会做些什么不同的事情。他的回答是:“我会把creat写成e。”
哈哈哈哈,这是一个笑话,这指的是unix的系统函数creat和文件打开标志O_CREAT。由于使用的是 creat 而不是 create 少写了一个 e ,所以Ken表示,要是我来写,那我就直接用e, creat都不用。其实就是调侃少写个字母。
于是便有了这么一个有趣的提交
该提交也只是在file.go内的文件标记常量中增加一行 O_CREATE = O_CREAT
Git
终于到我们熟悉的“伙伴”上了,到2014年后,Google Code Project Hosting 即将关闭,此时的Go代码需要一个新家,最后选择了 Gerrit Code Review,许多人认为 Go 托管在 GitHub 上,但 GitHub 只是问题跟踪器的主要来源:源代码的官方主要副本位于 go.googlesource.com
2014年12月8日,第一个Git提交出现,至此到今天,Go依然使用Git做为版本控制系统。
commit 369873c6e5d00314ae30276363f58e5af11b149c
Author: David Symonds <dsymonds@golang.org>
AuthorDate: Mon Dec 8 13:50:49 2014 +1100
Commit: David Symonds <dsymonds@golang.org>
CommitDate: Mon Dec 8 13:50:49 2014 +1100
convert .hgignore to .gitignore.
.hgignore => .gitignore | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
commit f33fc0eb95be84f0a688a62e25361a117e5b995b
Author: David Symonds <dsymonds@golang.org>
AuthorDate: Mon Dec 8 13:53:11 2014 +1100
Commit: David Symonds <dsymonds@golang.org>
CommitDate: Mon Dec 8 13:53:11 2014 +1100
cmd/dist: convert dist from Hg to Git.
src/cmd/dist/build.c | 100 ++++++++++++++++++++++++++++++---------------------
1 file changed, 59 insertions(+), 41 deletions(-)
commit 26399948e3402d3512cb14fe5901afaef54482fa
Author: David Symonds <dsymonds@golang.org>
AuthorDate: Mon Dec 8 11:39:11 2014 +1100
Commit: David Symonds <dsymonds@golang.org>
CommitDate: Mon Dec 8 04:42:22 2014 +0000
add bin/ to .gitignore.
Change-Id: I5c788d324e56ca88366fb54b67240cebf5dced2c
Reviewed-on: https://go-review.googlesource.com/1171
Reviewed-by: Andrew Gerrand <adg@golang.org>
.gitignore | 1 +
1 file changed, 1 insertion(+)
而这就是故事的结尾,直到在未来的某个时刻转向第五个版本控制系统。
Q.E.D.