【问题标题】:Git commits for each push每次推送的 Git 提交
【发布时间】:2020-01-20 07:19:45
【问题描述】:

有没有办法检索最新推送的提交列表?例如,假设我正在执行 10 次本地提交,但只有一次推送,是否有 git 命令仅显示这 10 次提交?

【问题讨论】:

  • 不是纯 git。您必须使用支持此功能的服务器。例如,我认为 BitBucket 显示了上次推送中的提交。可能其他人也这样做。
  • 有什么方法可以通过命令行实现吗?
  • 在推送之前,您可以列出遥控器上当前不存在的提交,这是您所追求的吗?
  • 为什么需要跟踪推送?
  • @LasseVågsætherKarlsen 不,我想检索远程存储库中按推送分组的提交(可能是一个,可能更多)。例如:我将在本地提交一个文件,然后对其进行修改,再次提交,添加另一个文件,提交然后推送这些本地提交。我想检索该推送的 3 个提交。

标签: git git-bash


【解决方案1】:

简短的回答是你不能可靠地做你想做的事:Git 本身不记录git push 动作。但是有些事情你可以做。具体来说,在 Git 执行 git push 或 Git 接收 git push在推送本身时,您可以获取此信息。如何保存、处理和以后使用,由您自己决定。

(我也认为这不是一个好主意:不要尝试通过 push 对事物进行分组,而是以其他方式对它们进行分组。例如,在 CI 系统中,将它们分组通过 request,请求动态更新。如果构建请求 #30 将 A、B 和 C 提交为“自请求创建以来的新”,则由于先前的推送,五秒钟后,但现在有 A 、B 和 D 而是对 ABD 进行 CI 检查,而不是对 ABC 进行 CI 检查,然后对 remove-C-add-D 进行检查。通读此答案的其余部分以了解此处发生的情况。)

预推钩

发送提交的 Git 将运行 pre-push 钩子(如果存在)。发送 Git 上的 pre-push 钩子每个获取四个信息项,嗯,我们暂时称它为“per thingy”:

  • 本地参考
  • 本地 OID/SHA-1/哈希
  • 远程参考
  • 远程 OID/SHA-1/哈希

假设你做到了:

git push origin refs/heads/master:refs/tags/v1.1

这里的本地引用是refs/heads/master。哈希 ID — 今天是 SHA-1 哈希,但 Git 内部现在称为“OID”(意思是对象 ID),以便在 Git 切换到 SHA-256 时进行验证,但您可以将其称为“哈希”以避免TLA 综合症1 — 是您的 refs/heads/master 标识的任何提交哈希 ID。远程引用将是refs/tags/v1.1,远程哈希可能是全零,因为这可能是您想要创建的一个新的轻量级标签。

如果你跑了:

git push origin master develop

你的钩子会得到两个东西。一个会提到refs/heads/master 两次,另一个会提到refs/heads/develop 两次:本地和远程master 分支,以及本地和远程develop 分支,你正在推动一个大的git push 操作。哈希 ID 将是您本地的 master 和他们的 master,以及您本地的 develop 和他们的 develop

使用这些哈希 ID,您可以查看哪些提交对他们来说是新的。如果他们的哈希 ID 在您的 Git 存储库中,您还可以查看您是否要求他们删除任何提交,或者更准确地说,让他们无法访问。有关可达性的更多信息,请参阅Think Like (a) Git

其中一些哈希 ID 可能全为零。这样的哈希 ID 意味着“没有这样的名称”。对于git push,如果您要求他们的Git 删除 引用,remote 哈希将全为零。如果您没有引用,则 local 哈希将全为零(这仅在您也要求它们删除时才有意义)。


1TLA 代表三个字母的首字母缩写词。与 ETLA 相比,ETLA 是一个超过三个字母的扩展 TLA。


pre-receive、update 和 post-receive 钩子

接收提交并被要求更新其引用的 Git 将运行 pre-receive 钩子和 post-receive 钩子(如果存在)。这些将获得与更新请求一样多的“东西”。它还将运行更新钩子(如果存在),每件事一次。

pre-receive 钩子为每个事物获取三个信息项:

  • 当前(旧)OID/哈希
  • 提议的新 OID/哈希
  • 参考文献

current 哈希告诉您名称当前代表什么。例如,在我们的标签创建示例中,当前哈希将全为零。建议的新哈希是推送 Git 要求您(接收 Git)用作更新引用的新哈希 ID 的对象 ID。引用当然是要更新的引用。

在我们的两个要更新的分支示例中,refs/heads/master 的两个哈希值将是 当前 master 提交和提议的新 master 提交。这些都可能是有效的散列,而不是全零,但最多有一个可以是全零。如果您(接收 Git)还没有引用,则旧哈希全为零(即分支 master 对您来说是全新的);如果您(接收 Git)被要求删除引用,则 new-hash 为全零。

预推送挂钩的工作是通读所有建议的更新并验证这是否正常。如果是这样,pre-push hook 应该退出 0(shell-exit-status-speak 中的“true”)。如果没有,pre-push 挂钩可以打印输出,旨在通知运行 git push 的用户 为什么 推送被拒绝 - 用户将看到此输出,其中包含单词 remote: 卡在前面它——然后退出非零,以拒绝整个推送。

在 pre-receive 钩子运行时,接收 Git 可以访问所有建议的对象。也就是说,如果执行推送的人运行git push origin master develop,这意味着发送三个新的master 提交和一个新的develop 提交,则服务器上的预接收挂钩运行服务器之后已收集所有四个新提交,以及这些提交所需的任何其他对象。新物品被“隔离”在某处的收容区。如果推送被拒绝,隔离区将被丢弃,而不会将提交合并到主存储库中。2整个推送在此阶段中止。

如果 pre-receive 钩子允许推送(或不存在),推送会进入下一个阶段,接收 Git 实际上确实更新每个引用,一次一个。此时接收 Git 为每个引用运行 update 钩子,给它(作为参数,而不是作为标准输入)引用、旧哈希和新哈希(注意不同的顺序)。更新挂钩可以像以前一样检查项目,然后接受或拒绝此特定更新。无论更新是否被拒绝,接收都会继续下一个引用。所以更新钩子只有一个本地视图——一次一个引用——但是更细粒度的接受/拒绝控制。

最后,在所有更新完成或被拒绝后,如果 any 引用被更新,接收 Git 运行 post-receive 钩子(如果存在)。这将获得与预接收挂钩相同的标准输入行。钩子应该退出零,因为推送已经完成。各种参考更新上的锁定已被释放,因此钩子不应在 Git 存储库中查找参考名称——它们可能已因另一次推送而更改!


2这个“隔离区”是 Git 2.13 中的新功能;在此之前,即使最终未被使用,新对象也会进入,但后来不得不被丢弃。在非常大的服务器(例如 GitHub)上,这会造成很大的痛苦。


枚举提交

给定一个旧的哈希 ID 和一个新的哈希 ID,命令:

git rev-list $old..$new

枚举所有可从$new 访问但不能从$old 访问的提交。例如,对于 git push,这些是刚刚添加的新提交。

它的对应物:

git rev-list $new..$old

枚举从$old 可访问的提交,这些提交不再可从$new 访问。例如,这些是被推送删除的提交。

请注意,两者可以同时进行!更新可能会删除一个提交并将其替换为新的和改进的变体。

您可以使用以下方法一次性获得两组提交:

git rev-list $old...$new

要使此输出有用,您必须添加--left-right 以插入标记,哪些提交只能从$old 访问,哪些提交只能从$new 访问。

您可以使用git rev-list --count 获取 counts 次可访问的提交。将--left-right 添加到三点变体会给您两个计数:例如,这就是git status 计算前后计数的方式。 (好吧,git status 已经编译了代码,所以它比在脚本中更容易——但这让你可以在脚本中执行 git status 所做的事情。)

结论

推送枚举是可能的,但只能通过使用 Git 仅在推送事件期间保留的信息。一旦推送完成或被拒绝,您就只有结果图。除了记录推送本身的关于的东西——例如,发送邮件通知某人推送事件添加了 3 个提交并删除了 1 个——这通常不是很有用,这就是 Git 不保留它的原因自己。

如果某个特定的提交分组有什么重要的地方,您可以将其记录在图表本身中。例如,假设您有一个需要三个步骤才能实现的功能:

  • 升级现有的无法使用的例程,让它们更有能力
  • 添加新的例程来做新的事情
  • 添加以新方式使用新旧例程的顶级集成

在这种情况下,而不是从:

...--o--*   <-- master

到:

...--o--*--A--B--C   <-- master

其中AC 是执行这三个步骤的新提交,考虑将新图推送为:

...--o--*---------M   <-- master
         \       /
          A--B--C

这里M 是一个新的合并提交。将其合并消息设置为(更好的变体)集成新功能。设置 A、B 和 C 的提交消息以增加现有例程添加新例程以及整合新旧例程以支持新功能。这个合并气泡——A-B-C 链——隔离了这个特性,所以如果真的很糟糕,你可以通过恢复 M 来恢复整个合并,如果有什么问题,你可以测试提交 A 到 @ 987654374@单独弄清楚是什么。您可以执行其中一项或两项操作——是否恢复整个合并;测试是否单独提交,因为所有信息都永远保存在图表中。

【讨论】:

    【解决方案2】:

    你说

    我正在做 10 次本地提交

    所以假设你做了一个

    git clone whatever
    git pull
    

    就在你做之前

    # edit stuff
    git commit -a
    # repeat 9 more times
    

    然后

    git push
    

    现在您可以使用命令查看这 10 个提交

    git log origin/master@{1}..origin/master
    

    如果你这样做了

    git fetch
    

    在两次提交之间的任何时候,您都已经破坏了您开始本地工作时 origin/master 所在位置的 reflog。您必须记住git fetch 更改origin/master 的频率并将{1} 调整为{2}(它计算了多少更新——而不是修订——你想返回)。

    【讨论】:

      【解决方案3】:

      感谢大家的支持,尤其是@torek 的聪明有趣的回答,这就是我使用 gitlab API 和 python 的方式:

      import json
      import requests
      
      def checkAsset(obj):
          status=0
          #status=0 modified, status=1 new file, status=2 deleted
          if (obj['new_path']==obj['old_path'] and obj['new_file']==False):
              status=0
          elif (obj['new_path']==obj['old_path'] and obj['new_file']==True):
              status=1
          elif (obj['new_path']==obj['old_path'] and obj['deleted_file']==True):
              status=2
          else:
              status=0
          return status
      
      
      headers = {'Private-Token': 'XXXXXXXXXXXXXX'}
      #this API gives you all commits grouped by pushes
      pushes= "https://gitlab.XXXXX/api/v4/projects/{{projectID}}/events??target_type=issue&action=pushed"
      
      r = requests.get(pushes, headers=headers)
      
      latestPushes=json.loads(r.content)
      
      lastPush=latestPushes[0]
      i=0
      while lastPush['push_data']['ref']!= 'master':
          i+=1
          lastPush=latestPushes[i]
      
      commitNumber=lastPush['push_data']['commit_count']
      if (commitNumber > 30):
          raise Exception("Could not compare, too many commits in one push")
      initCommit=lastPush['push_data']['commit_from']
      latestCommit=lastPush['push_data']['commit_to']
      
      compareApi= "https://gitlab.XXXXXXXXXXX/api/v4/projects/{{projectID}}/repository/compare?from="+str(initCommit)+"&to="+str(latestCommit)
      
      r = requests.get(compareApi, headers=headers)
      
      compareJson=json.loads(r.content)
      
      diffs=compareJson['diffs']
      
      Mlist=[]
      Alist=[]
      Dlist=[]
      for asset in diffs:
              status=checkAsset(asset)
              if status==0:
                  Mlist.append(asset['new_path'].encode('ascii','ignore'))
              elif status==1:
                  Alist.append(asset['new_path'].encode('ascii','ignore'))
              else:
                  Dlist.append(asset['new_path'].encode('ascii','ignore'))
      

      【讨论】:

        【解决方案4】:

        您可以使用以下命令检查您的提交历史记录。

        git log
        

        【讨论】:

        • 是的,我知道,但有没有办法只针对特定的推送?例如 repo 上的最新推送
        猜你喜欢
        • 2015-01-16
        • 2013-12-17
        • 2010-12-19
        • 2014-05-06
        • 1970-01-01
        • 2023-04-10
        • 1970-01-01
        • 2021-08-12
        • 1970-01-01
        相关资源
        最近更新 更多