【问题标题】:I can't push checkouts of previous commits to remote bare repo. Why?我无法将以前提交的签出推送到远程裸仓库。为什么?
【发布时间】:2017-08-02 10:02:06
【问题描述】:

我有一个本地(和主要)工作存储库。我还有一个用于部署目的的远程裸仓库(通过接收后挂钩)。

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

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

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 以上。但不知道会不会好。

【问题讨论】:

  • 看来您只是在签出现有的提交。除非你提交新的提交,否则没有什么可推送的。你能详细解释一下你想做什么以及你预计会发生什么吗?
  • @LasseV.Karlsen 我只希望远程裸仓库运行 post-receive 命令以进行先前的提交。这样远程 /path/to/site 目录将与先前的提交一起被签出。 post-receive 脚本是 git --work-tree=/var/www/html/site.com --git-dir=/path/to/remote/bare checkout -f
  • @horse 不,您不想要接收后挂钩,您自己说过! stackoverflow.com/questions/21640123/… 我引用“没有远程裸仓库就不可能吗?我是唯一的开发人员,我的本地仓库是唯一的仓库。我不想增加复杂性。”请参阅下面的答案
  • @VonC 是的,但是您说在远程服务器上没有裸仓库是不可能的。您说 --work-tree 无法获取远程路径,而只能获取本地(或共享)目录。因此,我尝试通过在远程服务器上使用裸仓库来找到解决方案。
  • @horse 我当时提出并在下面提出一个明确的替代方案,它不涉及裸仓库,不涉及 --work-tree:只需推送,您推送的内容将被签出。

标签: git github


【解决方案1】:

正如我在previous comments 中提到的,如果您想推送到非裸仓库,您需要,如“Git 2.4 — atomic pushes, push to deploy, and more”文章中所述,以及“Can git push to the current branch of a remote repository?”中所述:

  • 服务器端的 git 2.4 或更高版本(您要推送到的地方)
  • 你需要设置,仍然在服务器端:

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

正如我mentioned here,你可以在“Deploy a project using Git push”中看到一个具体的例子

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

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

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

【讨论】:

    【解决方案2】:

    作为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 分支,这意味着无论你当前的分支是什么——或者在这种情况下,不是——仍然没关系。

    你的 Git 调用他们的 Git——在这种情况下是你服务器的 Git;让我们将它称为 S for server——你的 Git 会告诉 S:“请设置你的,Smaster 提交 somehash"(其中 somehash 是存储在您的名字 master 中的任何哈希 ID)。 S 检查 他的 master 并且它已经设置为该哈希1 所以 S em> 什么都不做,然后说“好的,都完成了!”

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

    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 &lt;name&gt; &lt;hash&gt;。)

    这一次,你的 Git 像以前一样调用 S,但不是说“请将你的 master 设置为 hash”,而是说“请设置你的 @ 987654336@ 到 散列。”想必S还没有这样的分支;因为您使用的是 Git 版本 1.9,或者已将您的更高版本的 Git 配置为像旧的 Git 一样工作,所以您的 Git 最终会告诉 S创建该分支。同样地,S 确实 创建了那个分支,也就是说,这个推送成功了。现在我们必须看看另一台机器上发生了什么,即从S的角度来看发生了什么。

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


    1我在这里做另一个假设,即您之前成功推送了您的master,或者没有提交您的master


    S 上所见

    当 Git 电话响起时,您的服务器 S 正在安静地嗡嗡作响。2响铃!响铃! S 应答,这是自称是你的人打来的电话。另一个 Git 说“你好,请将你的 branch_from_previous_commit 设置为 somehash。”您 (S) 检查这是否正确,因此您创建了一个新引用 refs/heads/branch_from_previous_commit,并将其设置为 somehash。然后你会看到你有一个post-receive 钩子,所以你运行它,在它的标准输入上输入这一行(全部作为一行——为了显示的目的,我把它分成三行):

    0000000000000000000000000000000000000000
     somehashsomehashsomehashsomehashsomehash
     refs/heads/branch_from_previous_commit
    

    全零值是一个指示符:“此引用是新的”。非零值是新的哈希。引用名称refs/heads/branch_from_previous_commit 是分支的全名:post-receive 脚本可以判断它是一个 branch 名称,因为名称以refs/heads/ 开头,并且可以获取分支名称通过剥离 refs/heads/ 部分。

    如果您的电话要求您创建标签,则全名是refs/tags/sometag;如果它要求您创建六个分支,再更新三个分支,创建一个标签并删除两个分支,您会将所有 12 个名称连同它们的旧哈希 ID 和新哈希 ID 一起输入到 post-receive 脚本中。如果名称是新的,则旧的哈希 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:

    您可以省略&lt;branch&gt;,在这种情况下,命令会退化为“检查当前分支”,这是一个美化的空操作,具有相当昂贵的副作用,仅显示跟踪信息(如果存在)当前分支。

    当前分支是什么?请记住,我们是服务器S,在我们的 Git 存储库中工作。我们不是客户。我们正在接听 Git 电话,我们有我们自己的存储库,有自己的分支和自己的 HEAD。 我们的 HEAD 里有什么?

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

    请注意,如果我们收到将 我们的 master 更改为新的、不同的哈希的请求,我们会验证这样做是可以的,然后就完成了(前提是可以) ,然后运行我们的post-receive 脚本,该脚本将检查我们的(更新的)master。那真的做点什么。但这不是我们得到的,也不是我们所做的。

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

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

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

    3. 更改服务器上的当前分支。如果部署命令是git checkout 没有分支名称,它将部署当前分支。如果您以某种方式更改它,它将部署不同的提交。如何做到这一点是一个不同的问题,但请注意git checkout &lt;branch-name&gt; 更改了当前分支(同时还检查了该分支的提示提交)。如果您选择喜欢部署脚本,这可能会影响您。

    在任何情况下,最好至少一点点设计部署脚本,以扫描正在更新的分支和/或标签,然后运行在待部署的分支发生更改时签出部署。这不是必需的,但如果您的服务器将保留多个分支和/或标签,那么它是一个好主意,并且/或标签,只有一个或一些要部署。


    2实际上,可能是 ssh-phone 或 https-phone,然后有其他东西接听,最终在确定 是你之后,将手机交给转到 Git。但我们不必担心这一点。

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

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

    【讨论】:

    • 不,不是这样:您错过了触发该问题的上下文。 stackoverflow.com/questions/21640123/…。这是关于在结帐时正确配置部署。仅此而已。
    • 嗯,这给出他的当前设置的答案。我不是在这里讨论替代方案,只是因为这种特定的部署方法不能以这种方式工作。
    猜你喜欢
    • 2019-06-29
    • 2011-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-22
    • 1970-01-01
    • 2017-02-04
    相关资源
    最近更新 更多