当有多个作者使用版本控制时,如何跟踪库的构建计数?

时间:2011-10-10 13:26:41

标签: git svn version-control merge build-numbers

我不知道这是否是人们常做的事情,但我个人总是记录我构建代码的次数。也就是说,我调用make的次数和构建成功的次数。

我目前的解决方案

我有一个简单的代码,它将文件作为参数,打开它,增加内部数字并覆盖它。编译此代码时,首先调用make

在调用./increase_build build.txt之后,立即调用make来构建库的次数增加。

然后,编译代码并生成lib文件(使用ar cq ...)。之后,调用./increase_build libbuild.txt,增加成功构建的数量。最后建立了测试。

This是我的一个Makefile的示例。

为什么我关心

这一直很好,直到我开始使用版本控制。似乎没有问题:我是我自己的库的唯一作者,我逐个添加功能。

有一天,我正在测试分支和合并(我在工作中使用git和svn),所以我在分支中添加了一个功能,并在master中更改了一些内容,并将两者合并。现在,构建计数文件具有不同的值。

问题

问题是,假设在分支时,构建计数为100.然后我在分支中写入内容并且构建计数达到110.我在master分支中写入内容并且构建计数达到120。我合并了两个,我看到一个是110而一个是120(顺便说一句是冲突)。正确的解决方案是将构建设置为130。

然而,我不能(读我不想)回到分支起飞的提交,发现它是100所以我计算100+(110-100)+(120-100) = 130!我希望这是自动的

问题

问题很明显:我该怎么做?当我使用版本控制时,如何跟踪构建计数(不提交计数!)?我不想要基于版本控制中的功能的答案,因为如果我更改版本控制系统,问题就会重新出现。

我能做的就是在构建计数文件中为每个构建添加一行,比如数据和时间。然后构建号将是构建计数文件中的行数。另外,除非我在两个分支上得到两个完全相同的时间,然后合并文件只是两者的结合。

我不知道,有没有更好的解决方案呢?我想要的(构建计数)是否值得付出努力?

P.S。如果你想知道我为什么用构建数量和成功构建的数量来做这件事,那只是个人的事情。我喜欢看到我在编写代码时遇到的小错别字和错误的重建次数。

编辑:我使用C和C ++进行编程,因此解决方案对我有用。

4 个答案:

答案 0 :(得分:2)

由于内部版本号不是您所在分支的功能,因此应以不同方式跟踪它。我不使用git,但对于SVN,我们有一个系统在工作,通过将其复制到特定标签来构建特定分支,添加一些特定于该标记的伪像(您的构建编号将是这种类型的主要示例)要添加的东西),并且仅在构建成功时提交。

换句话说,有一个指定的地方(SVN中的标签名称,或者它可能是一个单独的仓库),你只进行构建,这是你构建的唯一地方,那就是内部版本号信息被存储和更新。您的构建脚本看起来像

# I don't know git -- this is all very much pseudocode

# Where did you commit the code you want to build?
source=git://server/path/to/my/branch

# Replace builddir tree with yours
git replace git://server/special/place/build/thisproject with code from $source

cd /tmp
git checkout git://sever/special/place/build/thisproject into new builddir
cd builddir

update local version-controlled file buildnumber+=1

if make
    # Build was successful
    git commit buildnumber
    copy build artefacts to where-ever
endif

cd /tmp
rm -rf /tmp/builddir      

有竞争条件;如果有人在您之后检查构建请求,但不知何故最终到达服务器,您将最终建立他们的签到。

使用像Hudson / Jenkins这样的指定构建主机可能会更加简单。

答案 1 :(得分:1)

使用构建日志(每个构建一行)的解决方案看起来非常聪明。您可以添加执行构建的计算机的IP(或Mac地址),这样您就可以消除重复行的风险。但是,根据您的VCS,您可能需要手动合并构建日志文件。 使用git,您可以对其进行配置,以便合并将始终保留两个版本(并最终按日期排序等)。

答案 2 :(得分:1)

我将发布并接受我自己的想法的实施作为答案,因为它似乎是最实用的。

所以这是解决方案:

  • 对于每个构建,在构建文件中添加一行,其中包含以下数据:
    • 日期
    • 时间
    • 随机数
  • 每次合并时,都要保留两个构建文件中的行
  • 构建数是构建文件中的总行数。

构建文件的每一行都必须是唯一的。日期和时间几乎是独一无二的。两个人不太可能同时在自己的分支上发布构建。但是,它可能会发生。因此,生成并添加随机数以减少该机会。

但是有一个问题。如果您使用srandtime(NULL)进行种子处理,那么由于两个构建版本应该同时进行,因此生成的数字也可能恰好相同。因此,随机数生成器可以用不同的数字播种,例如clock()gettimeofday()的毫秒部分。即使不是随机生成的,也可以放置这些数字而不是随机数。

如果两行仍然相同,我将应用ostrich algorithm

<强>更新

我实现了它,一切正常。最后,我使用clock_gettime(CLOCK_MONOTONIC, ...)并将此函数获得的纳秒打印为随机数。我没有使用clock()的原因是因为程序很短,所以它的运行速度低于clock()的分辨率,因此我保持0分。

<强>更新

这是我写的最终代码(其中某些部分是从其他地方偷来的!)。在某些平台上可能需要-lrt

/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef _WIN32
#include <windows.h>

struct timespec
{
    long tv_sec;
    long tv_nsec;
};

/* Note: I copy-pasted this from internet (https://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows/5404467#5404467)
 * I tweaked it to return nanoseconds instead of microseconds
 * It is much more complete than just finding tv_nsec, but I'm keeping it for possible future use. */
LARGE_INTEGER getFILETIMEoffset(void)
{
    SYSTEMTIME s;
    FILETIME f;
    LARGE_INTEGER t;

    s.wYear = 1970;
    s.wMonth = 1;
    s.wDay = 1;
    s.wHour = 0;
    s.wMinute = 0;
    s.wSecond = 0;
    s.wMilliseconds = 0;
    SystemTimeToFileTime(&s, &f);
    t.QuadPart = f.dwHighDateTime;
    t.QuadPart <<= 32;
    t.QuadPart |= f.dwLowDateTime;
    return t;
}

int clock_gettime(int X, struct timespec *tv)
{
    LARGE_INTEGER t;
    FILETIME f;
    double microseconds;
    static LARGE_INTEGER offset;
    static double frequencyToNanoseconds;
    static int initialized = 0;
    static BOOL usePerformanceCounter = 0;

    if (!initialized)
    {
        LARGE_INTEGER performanceFrequency;
        initialized = 1;
        usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency);
        if (usePerformanceCounter)
        {
            QueryPerformanceCounter(&offset);
            frequencyToNanoseconds = (double)performanceFrequency.QuadPart/1000000000.0;
        }
        else
        {
            offset = getFILETIMEoffset();
            frequencyToNanoseconds = 0.010;
        }
    }
    if (usePerformanceCounter)
        QueryPerformanceCounter(&t);
    else
    {
        GetSystemTimeAsFileTime(&f);
        t.QuadPart = f.dwHighDateTime;
        t.QuadPart <<= 32;
        t.QuadPart |= f.dwLowDateTime;
    }

    t.QuadPart -= offset.QuadPart;
    microseconds = (double)t.QuadPart/frequencyToNanoseconds;
    t.QuadPart = microseconds;
    tv->tv_sec = t.QuadPart/1000000000;
    tv->tv_nsec = t.QuadPart%1000000000;
    return 0;
}

#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 0       /* not used anyway */
#endif
#endif

int main(int argc, char **argv)
{
    time_t now_sec;
    struct tm *now;
    FILE *bout;
    struct timespec now_clk;
    if (argc < 2)
    {
        printf("Usage: %s build_file_name\n\n", argv[0]);;
        return EXIT_FAILURE;
    }
    bout = fopen(argv[1], "a");
    if (!bout)
    {
        printf("Could not open file: %s\n\n", argv[1]);
        return EXIT_FAILURE;
    }
    time(&now_sec);
    now = gmtime(&now_sec);
    fprintf(bout, "%02d/%02d/%04d %02d:%02d:%02d", now->tm_mday, now->tm_mon+1, now->tm_year+1900, now->tm_hour, now->tm_min, now->tm_sec);
    clock_gettime(CLOCK_MONOTONIC, &now_clk);
    fprintf(bout, " %ld\n", now_clk.tv_nsec);
    return EXIT_SUCCESS;
}

希望这会对某人有所帮助。

更新

使用它约9个月后,我可以说这非常有用。一些观察结果是:

  • 在Windows上,clock_gettime实现给出的最后一个元素非常小,有一半的时间具有相同的值。然而,它仍然使它更随机。
  • 在Linux上,最后一个元素确实是随机的。
  • 我不得不做一个“Build”提交只是为了让构建文件中的行被提交,所以我可以合并。但是,使用git stash可以避免这种情况。
  • 使用它几乎总是会在合并时产生冲突,但解决它时非常简单(只需删除差异标记,因为需要两个文件中的行)。
  • wc -l是你的朋友。

答案 3 :(得分:-1)

  

我不能(读我不想)回到分支起飞的提交,发现它是100,所以我计算...我希望这是自动的

肮脏的想法:每个构建添加字符串到版本化文件(或根据结果对PASS / FAIL一对),每个分支略有不同。合并分支将需要手动合并此信号文件,其中字符串装饰的差异使此任务更容易。 wc -l以后会计算数字