4

我自己和另一位开发人员正在开发一个可由其他代码访问的 API。当我们更改 API 的行为以更好地满足我们的需求时,我们会在不弃用旧版本的情况下发布 API 的其他版本,这样使用 API 的现有应用程序就不必立即更新。例如:

$ cat 0.5.php
<?php
$username = $_POST['name'];
?>

$ cat 0.6.php
<?php
$username = $_POST['username'];
?>

当我们开始一个新版本时,通常我们会将cpN-1.php 版本转换为 N.php 并从那里编写代码。但是,如果我们使用 Git 执行此操作,那么我们会从文件中丢失整个blamediff和其他历史记录,以便进行比较和恢复。我怎样才能将旧文件的历史“伪造”到新文件中,使得blame, log,diff和这样的命令“正常工作”而不向它们提供额外的标志或参数,例如--follow

4

6 回答 6

10

你想使用-C标志。这将检测副本以及重命名,因此它可以遵循历史记录。 diff, blame, 并log接受这个标志。

就像@madara-uchiha 说的那样:你应该看看使用标签,也许git-x.y从它们生成你的文件。您可以使用类似以下的内容来获取给定标签处文件的内容:

git show v0.6:git.php > git-0.6.php

v0.6您感兴趣的标签在哪里。

更新

这是一个小脚本来做到这一点。第一个假设您的标签是以下形式x.yx.y.z

#!/bin/bash
versions=$(git tag -l | egrep -o '^[0-9]+\.[0-9]+(\.[0-9]+)?$')
for version in $versions
do
    git show $version:git.php > git-$version.php
done

如果您的标签是形式vX.YorvX.Y.Z并且您想要git-x.y.phporgit-x.y.z.php作为文件名,则此方法有效:

#!/bin/bash
versions=$(git tag -l | egrep -o '^v[0-9]+\.[0-9]+(\.[0-9]+)?$')
for version in $versions
do
    git show $version:git.php > git-${version/#v/}.php
done

在发布过程中运行此脚本,它将为您生成所有版本。git-此外,从名称中删除 非常容易。例如,> git-$version.php变成> $version.php

于 2013-06-27T12:49:40.803 回答
6

我认为您对此有点模糊,因为您似乎正在尝试将版本控制处理事物的方式与公开 API 的方式(即 Web 服务器如何处理事物)结合起来。

为了使 API 的多个版本同时工作,消费者可能需要指定他们想要用于给定调用的版本。出于此答案的目的,我假设您正在以与 Stack Exchange API 类似的方式工作,因此该版本被指定为 API URL 的第一个“目录”组件(例如,对于 1.5 版,我将我的请求定向到http://domain.tld/1.5/call,我使用的版本 1.6http://domain/1.6/?method=call等)。但实际上,这个元素并不重要,只要您有某种机制来确定适当的版本并将请求路由到 Web 服务器级别的正确控制器即可。

版本控制

我在这里采用的方法相当简单。每个版本在存储库中都有自己的分支。针对该版本执行的任何开发工作要么在版本分支的分支中完成,要么直接提交给该版本。Master 始终包含最新的稳定版本。

例如,假设当前版本是 1.5,并且当前所有内容都在 master 之下,并且您没有历史分支。在当前稳定代码下画一条线,并创建一个名为 1.5 的分支。现在,要在 1.6 上开始开发,它将在 1.5 分支上构建,从 master 创建一个新分支并将其命名为 1.6。

任何针对 1.6 的开发都发生在 1.6 分支中,或者使用 1.6 作为基础创建的其他分支。这意味着一切都可以很好,并且可以根据需要干净地推/拉到 1.6 分支中。

如果您需要在 1.5 版本中应用小错误修复,您可以在 1.5 分支中轻松完成此操作。如果您想从 1.6 分支中提取提交,则需要“挑选”它 - 由于分支已经开始分歧,任何此类问题都需要手动处理,以确保最大程度地安全保护“稳定“代码库。

当需要创建 1.7/2.0/whatever 时,将 1.6 版本拉入 master,标记它,并为新版本创建一个新分支。

以这种方式,每个版本/发布的谁做了什么以及什么时候做了什么的完整历史被存储在分支中。正如其他人所提到的,不要忘记标记您的里程碑版本。

网络服务器

使用上述方法,Web 服务器设置维护起来相当简单。每个版本的根目录都简单地与适当的分支同步。

因此,为了简单起见,我们假设版本控制中存储库的根目录对应于 API 代码的文档根目录(实际上这不太可能,但稍微重写一下 URL 或类似的方法可以解决这个)。

在 Web 服务器上域的文档根目录中,我们创建以下目录结构:

<文档根>
    |
    |--- 1.5
    |
    |--- 1.6

我们从中央版本控制克隆存储库到每个 1.5、1.6 目录,然后切换到适当的分支。每次您希望实时推送更改时,只需从相应分支的版本控制中下拉更改即可。

在大容量环境中,您可能有一个专用于为每个版本提供服务的完整服务器,其中版本标识符作为子域,但同样的一般原则适用 - 除了存储库可以直接克隆到每个服务器的文档根目录中。

很多(如果不是全部)为新分支创建目录、将 repo 克隆到其中并切换到适当的分支以及为发布拉下补丁/错误修复的过程可以使用脚本/cron 等自动化,但是在你这样做之前不要忘记:在没有人工参与的情况下将更改推送到实时服务器通常会以泪水告终。


另一种方法

...将创建一个单独的父存储库,作为域的文档根。在此,您将在存储库的根目录中为每个版本创建子模块。这将产生的总体效果将非常相似,但具有只需要在服务器上同步单个存储库的“优势”,并保持由版本控制定义的 Web 服务器的目录结构。但是,就个人而言,我不喜欢这种方法,原因有两个:

  • 子模块很难维护。它们附加到特定的提交,很容易忘记这一点。
  • 我相信分支驱动方法提供的控制更加精细,并且更清楚地知道到底发生了什么。

我承认这两个原因在很大程度上都是个人喜好,这就是为什么我提出它作为一种可能性。

于 2013-06-27T13:41:30.680 回答
2

警告:以下命令会重写共享存储库中最不希望出现的历史记录。在你按下 -f 之前三思。

重写您的历史记录以包含该文件的第二个副本git filter-branch

git filter-branch --tree-filter 'if [ -f 0.5.php ]; then cp 0.5.php 0.6.php; fi' HEAD

现在 0.6.php 是整个历史上 0.5.php 的完全副本。


然后,您的同事需要处理新的历史记录。

有人将变基或重置推送到已发布的分支后,我如何恢复/重新同步?

于 2013-07-28T05:56:56.543 回答
2

这是一个愚蠢的黑客,但它似乎接近你想要的行为。请注意,它假定您已将您关心的 0.5.php 的最早提交标记为first

  1. 分支

    % git checkout -b tmp

  2. 制作 0.5.php 文件的提交历史的补丁文件夹和补丁版本

    % mkdir patches && git format-patch first 0.5.php -o patches

  3. 删除您的文件并签出它的第一个副本

    % rm 0.5.php && git checkout first -- 0.5.php

  4. 重命名你的文件

    % mv 0.5.php 0.6.php

  5. 调整您的补丁文件以使用新名称

    % sed 's/0\.5\.php/0\.6\.php/g' -i patches/0*

  6. 提交(如果你还没有几次)

    % git add -A && git commit -m'ready for history transfer'

  7. 应用补丁

    % git am -s patches/0*

  8. 返回master,将新文件拉过来并删除tmp分支

    % git co master && git co tmp -- 0.6.php && git branch -D tmp

瞧!您的 0.6.php 文件现在具有复制您的 0.5.php 文件的历史记录,但 0.6.php 历史记录中的每个提交都将具有 0.5.php 历史记录中的唯一 ID。时间和责任应该仍然是正确的。通过一些额外的努力,您可能可以将所有这些放在一个脚本中,然后将该脚本别名为git cp.

于 2013-07-23T03:32:19.270 回答
1

更标准的方法是只有一个api.php文件,并使用分支和标签来标记新版本。

至于提供文件:如果您想为您的用户提供多个版本的 api,请使用一些部署过程来检查和构建您的 api 的特定版本,根据需要重命名和移动,并设置对此的公共访问 - 不要你的开发树。

于 2013-06-27T14:21:17.150 回答
1

查看git subtree也在此处)。有了它,您必须能够使用该文件拆分部分历史记录。如果机器人使用交互式变基,您也可以复制它。然后你可以将它合并回来,并拥有副本。

于 2013-06-27T12:51:29.467 回答