使用 Git 生成编译版本号

一般而言,我们的软件版本号通常会包括一个编译版本号。如果你的代码使用版本控制系统进行管理(很多开发者都是这么做的),这个编译版本号可以是我们版本控制系统的提交版本。

如果我们使用 Git 进行管理,这个版本号会是一个 40 位的 SHA-1 的哈希值。不过,要是我们的软件版本号添加这么一个 40 位长的字符串,显然是不合适的。所以我们一般会取前 7 位——如果这样还不能标识出唯一版本,那么就取前 8 位等等。不过,手动去修改这个值当然是不合适的,我们有更简便的方式。这就是本文所要介绍的内容:使用 Git 生成这个编译版本号。

为达到这一目的,我们一般会新建一个 version.h 文件,其内容可以是一个宏定义:

#ifudef VERSION_H
#define VERSION_H

#define VERSION_NUMBER 1.0.0

#endif // VERSION_H

然后在我们的程序中,通过 include 这个 version.h,就可以获取这个VERSION_NUMBER的值;而我们所要做的,就是能够由程序生成这个宏的值。这就是思路,下面我们来看看如何实现。

我们需要同 Git 进行交互,并且要能够写入文件。最方便的方式是使用 shell 脚本。这一设想是现实的:首先,Linux 天生具有 shell,可以直接使用;其次,Windows 虽然没有 shell,但是如果你要在 Windows 下使用 Git,就必须安装一个模拟环境,msys 或者 Cygwin,而这两个环境都提供了 shell。当然,如果你使用 msysgit 或者其他方式集成 git,那么就已经提供了这个 shell。因此,为了使用 shell 脚本,我们无需在拥有 Git 的开发环境中额外安装其他环境。这也是我们提到的这个方法的可取之处。

首先我们要提供一个“模板” version.h.template,用于生成 version.h:

#ifudef VERSION_H
#define VERSION_H

#define VERSION_NUMBER $FULL_VERSION

#endif // VERSION_H

我们的目的是能够自动替换$GIT_VERSION这个占位符,从而达到生成 version.h 的目的。

下面就是我们的 shell 文件。该文件参考了这段代码,在此表示感谢!

#!/bin/bash
rm -f src/version.h
git rev-list HEAD | sort > config.git-hash
LOCALVER=`wc -l config.git-hash | awk '{print $1}'`
if [ $LOCALVER \> 1 ] ; then
    VER=`git rev-list origin/master | sort | join config.git-hash - | wc -l | awk '{print $1}'`
    if [ $VER != $LOCALVER ] ; then
        VER="$VER+$(($LOCALVER-$VER))"
    fi
    if git status | grep -q "modified:" ; then
        VER="${VER}M"
    fi
    VER="$VER $(git rev-list HEAD -n 1 | cut -c 1-7)"
    GIT_VERSION=r$VER
else
    GIT_VERSION=
    VER="x"
fi
rm -f config.git-hash

cat version.h.template | sed "s/\$FULL_VERSION/$GIT_VERSION/g" > src/version.h

echo "Generated version.h"

下面来解释一下这个 shell 脚本。

第一行,说明是一个 bash 脚本。

第二行,强制删除 src/version.h 文件。可以想象,这个文件就是我们要生成的文件。如果之前已经生成过,我们则将其删除。

第三行,运行 git 命令。git rev-list HEAD 获取推送到服务器内容的提交列表,然后使用管道 | 将其结果发送给 sort 命令进行排序,之后使用 > 作输出重定向,生成 config.git-hash 文件。

第四行,调用一个 shell 命令:使用 wc 计算 config.git-hash 的行数,然后使用 awk 将其输出到一个变量 LOCALVER 中。

第五行到第十四行,如果 $LOCALVER > 1 —— 注意有 > 的转义;并且,在变量赋值时无需 $ 符号,但是在使用时需要添加 $ —— 那么使用“git rev-list origin/master | sort | join config.git-hash – | wc -l | awk ‘{print $1}’”对 VER 进行赋值。这一段命令我们前面已经解释过,这里不解释。如果 $VER != $LOCALVER,则将 VER 与 LOCALVER 拼接在一起。然后我们检查 git status 是不是 modified,如果是的话则在后面添加一个 M。然后,我们使用 cut 命令取前七位。最后,我们将这个值赋给 GIT_VERSION。注意,我们在 $VER 前面增加了一个 r,当然你也可以不加,根据自己的需要。

第十五行到第十八行,如果 $LOCALVER <= 1,则直接给 GIT_VERSION 和 VER 初始值。

第十九行,删除 config.git-hash 文件。

第二十一行,使用 cat 命令打开模板文件,利用管道将其传递给 sed——一个无需打开文件即可编辑的编辑器——发给 sed 的命令是“s/\$FULL_VERSION/$GIT_VERSION/g”,s 代表替换,s/AAA/BBB/ 表示将 AAA 用 BBB 替换,g 表示全局。注意,这里的 AAA 是正则表达式,因此,我们在查找文件中的 $FULL_VERSION 的时候,需要将 $ 转义。而后面的 $GIT_VERSION 则是取 shell 变量的值。最后输出重定向到 src/version.h 文件。

好了,现在运行下 version.sh,如果没有路径问题,version.h 已经在 src 目录下了!

Windows 平台注意:如果在 msysgit 下面,可能会有 join 命令找不到。出现这种问题是因为 msysgit 并没有把所有的 shell 命令都安装上去。解决方案是安装完整的 msys 或者 cygwin,然后将其中的 join.exe 放到 %GIT_PATH%/bin 即可。如果你不想或者不会安装庞大的 msys,那么可以按照这里所说的,利用 wget 下载这些命令。然后将其中的 join.exe 放到 %GIT_PATH/bin% 中。如果还有另外的命令找不到,按照这种方法即可。

4 Comments

  1. lee 2015年6月10日
    • 豆子 2015年6月11日
  2. lee 2015年6月11日
  3. lee 2015年6月11日

Leave a Reply