我无法将先前提交的结账推送到远程裸仓库。为什么?

时间:2017-03-12 09:35:44

标签: git github

我有一个本地(和主要)工作回购。我还有一个用于部署目的的远程裸仓库(通过post-receive hook)。

当我将新提交推送到远程仓库时,它可以正常工作。

但是我希望能够在新提交中出现问题时轻松回滚。为此,我在我的本地目录中尝试:

git checkout <sha1_of_previous_commit>
git push remote_repo master

没有任何改变。我也这样做:

git checkout <sha1_of_previous_commit>
git checkout -b branch_from_previous_commit
git push remote_repo branch_from_previous_commit

没有任何改变。

我也这样做:

git checkout <sha1_of_previous_commit>
git checkout -b branch_from_previous_commit
// I modified a file.
git add .
git commit -m "a commit from branch_from_previous_commit"
git push remote_repo branch_from_previous_commit

没有任何改变。

注意:Git版本是1.9,我尝试将其升级到2.4以上。但我不知道是否可以。

2 个答案:

答案 0 :(得分:0)

正如我在previous comments中所提到的,如果你想要推出一个非裸露的回购,你需要,如&#34; Git 2.4 — atomic pushes, push to deploy, and more&#34;文章,但也在&#34; Can git push to the current branch of a remote repository?&#34;:

  • 服务器端的git 2.4或更多(你要去的地方)
  • 您需要设置,仍在服务器端:

    cd /path/to/target/repo
    git config --local receive.denyCurrentBranch updateInstead
    

在我mentioned here时,你可以在&#34; Deploy a project using Git push&#34;

中看到一个具体的例子

这假设您正在推送您控制的服务器(不是github.com或bitbucket.org或gitlab.com)
You mentioned

  

我是唯一的开发者,我的本地回购是唯一的回购。我不想增加复杂性。

这就是为什么在这种情况下直接推送到非裸露的rpeo就好了。

答案 1 :(得分:0)

作为Lasse V. Karlsen said in a comment,第一种情况没有任何反应,因为您没有向遥控器推送任何更新。

当你运行git push时,你告诉你的Git通过网络电话拨打另一个Git并给它一些项目和/或说明。您有两个示例:git push remote_repo mastergit push remote_repo branch_from_previous_commit。让我们先拿第一个。

您使用的完整序列是:

git checkout <sha1_of_previous_commit>
git push remote_repo master

第一步是Git称之为分离的HEAD ,这意味着您现在不在分支机构上,因此您所做的任何新提交都将在不更改任何现有提交的情况下进行分支机构。你没有做任何事,所以这没关系,然后你告诉你的Git告诉他们的Git关于你的master分支,这意味着无论你目前的分支是什么 - 或者在这种情况下,isn& #39; t仍然无关紧要。

在这种情况下,你的Git会调用他们的Git - 你的服务器的Git;让我们将 S 称为服务器 - 你的Git告诉 S :&#34;请设置你的, S master提交 somehash &#34; (其中 somehash 是您的名称master中存储的任何哈希ID)。 S 检查他的 master,并且已经设置为该哈希 1 所以 S 什么都不做,并说&#34;好的,一切都完成了!&#34;

你的第二个序列做得更多:

git checkout <sha1_of_previous_commit>
git checkout -b branch_from_previous_commit
git push remote_repo branch_from_previous_commit

这里,您的第一个命令像以前一样分离HEAD,但是您的第二个命令在您自己的存储库中创建一个 new 分支名称,指向当前提交,这是您在第一个提交时检出的命令。 (顺便提一下,您可以将这两者合并为git checkout -b <name> <hash>。)

这次,您的Git像以前一样调用 S ,但不是说&#34;请将您的master设置为哈希&#34; ,它说&#34;请将您的branch_from_previous_commit设置为哈希。&#34;据推测, S 还没有 这样的分支;因为你使用的是Git 1.9版,或者你的后来的Git配置得像你的老Git,你的Git最终告诉 S 创建那个分支。同样可能, S确实创建了该分支,即此推送成功。现在我们必须看看在另一台机器上发生了什么,即从 S 的观点发生了什么。

(有关与您当前问题无关的更多详细信息,请参阅What does GIT PUSH do exactly?

1 我在这里做了另一个假设,即您之前成功推送了master,或者未对master做出任何提交。

S

所示

当Git-phone响起时,你的服务器 S 正在悄悄地哼着声。 2 响铃!戒指! S 回答,这是来自自称是你的人的电话。其他Git说&#34;您好,请将您的branch_from_previous_commit设置为 somehash 。&#34;您( S )检查这是否正常,因此您创建了一个新引用refs/heads/branch_from_previous_commit,设置为 somehash 。然后你看到你有一个post-receive钩子,所以你运行它,在它的标准输入上将这条线(全部作为一条线 - 我把它分成三个用于显示目的)喂它:

0000000000000000000000000000000000000000
 somehashsomehashsomehashsomehashsomehash
 refs/heads/branch_from_previous_commit

全零值是一个指标:&#34;此引用是新的&#34;。非零值是新哈希值。引用名称refs/heads/branch_from_previous_commit是分支的全名:后接收脚本可以告诉它&#39; 分支名称,因为名称以refs/heads/开头,并且可以通过剥离refs/heads/部分来获取分支名称。

如果您的电话要求您创建标记,则全名为refs/tags/sometag;如果它要求您创建六个分支,更新三个分支,创建一个标记,并删除两个分支,您将使用旧的和新的哈希ID将所有十二个名称提供给接收后脚本。如果名称是新的,则旧的哈希ID全为零;如果要删除名称,则新的哈希ID为全零;否则两个散列ID都是非零的,它们是更新的分支或标记的旧值和新值。 3

你没有向我们展示你的脚本,所以现在我必须猜测编辑:你在脚本中添加了一条注释:

git --work-tree=/var/www/html/site.com --git-dir=/path/to/remote/bare checkout -f

(甚至没有读取标准输入)。此脚本错误 4

git checkout命令包含选项,包括-f--force,但也包括分支名称。 此脚本提供的分支名称是什么?

If a git checkout command is given no branch name, what branch does Git check out? Click on the documentation link to see the following text:

  

你可以省略<branch>,在这种情况下,命令退化为&#34;检查当前分支&#34;,这是一个美化的无操作,带有相当昂贵的副作用,只显示跟踪当前分支的信息(如果存在)。

什么是当前分支?请记住,我们是服务器 S ,在我们的Git存储库中工作。我们不是客户。我们正在回答Git-phone,我们有我们自己的存储库,它有自己的分支和自己的HEAD。什么在我们的 HEAD?

我们的HEAD可能包含分支名称master。所以我们将再次检查我们的master,做(如文档所述)一种昂贵的无操作。我们刚刚收到创建新分支branch_from_previous_commit的请求这一事实非常无关紧要:我们被告知要查看当前分支,所以我们做了。

请注意,如果我们收到将我们的 master更改为新的不同哈希的请求,我们就会确认这样做是可行的,然后完成它(前提是它)单击确定),然后运行我们的post-receive脚本,该脚本将检出我们的(更新的)master 实际上会做点什么。但那不是我们得到的,而不是我们所做的。

因此,您需要做的至少是以下其中一项:

  1. 修改接收后脚本:了解部署不同分支的含义。这比看起来更难,因为每个Git存储库都有一个主索引,而Git假定索引与工作树匹配。如果设置了多个部署工作树,则必须为每个此类树设置多个索引文件,否则将使索引无效。

  2. 使用修改master分支的推送。

  3. 更改服务器上的当前分支。如果部署命令为git checkout而没有分支名称,则它将部署当前分支。如果你以某种方式改变它,它将部署一个不同的提交。如何实现这一点是一个不同的问题,但请注意git checkout <branch-name>更改当前分支(同时还检查该分支的提示)。如果您选择想要部署脚本,这可能会对您产生影响。

  4. 在任何情况下,可能明智地想要部署脚本至少一点点,以扫描哪些分支和/或标记正在进行更新,如果要部署的分支已更改,则仅运行checkout-to-deploy 。这不是必需的,但如果您的服务器将保留多个分支和/或标记,只有一个或部分将被部署, 是个好主意。

    2 实际上,它可能是ssh-phone或https-phone,还有别的东西可以回答,最终在决定 你之后,把手机交给Git。但我们并不担心这一点。

    3 标签更新需要--force,因为Git 1.8.2无论如何。当且仅当它们是非快进时,分支更新才需要强制。除了这些内置规则,您的pre-receiveupdate挂钩可以拒绝所有(预接收)或选定(更新)引用更改。

    4 或者至少是次优的,尽管它最初会完成工作。