【问题标题】:Identifying actual branch names of git commits识别 git 提交的实际分支名称
【发布时间】:2019-01-31 03:33:46
【问题描述】:

这是一个简单的 git 存储库。我已经用数字标记了提交,以便于参考。该仓库有以下分支:

  • master: 13 次提交 (1,2,3,4,5,6,7,8,9,10,11,12,13)​​
  • new_branch:8 次提交 (1,2,3,4,5,6,14,15)
  • test_branch:3 次提交 (1,2,3)
  • yet_another_branch:14 次提交(1,2,3,4,5,6,7,8,9,10,11,12,16,17)

提交 5,6 属于拉取请求,因此带有 5,6 的蓝色部分不是分支。

请注意,提交 1,2 被视为所有分支的一部分,但我想将所有黑色提交视为主分支的一部分。同样,对于“测试分支”,我只想将提交 3 视为分支的一部分。

from git import Repo

git_url = "https://github.com/unimamun/test-repo.git"
repo_dir = "/mnt/hdd/aam/J2_Repos/test-repo/test-repo"

repo = Repo.clone_from(git_url, repo_dir)

# get all commits by branches    
def get_commits(repo, ref_name):
    commits = []
    for commit in repo.iter_commits(rev=ref_name):
        commits.append(commit)
    return commits

print('\nCommits in Branches:')
for ref in repo.references:
    print(ref.name,': ', str(len(get_commits(repo, ref.name))))


print('\nCommits in master:')
commits = list(repo.iter_commits('master'))
commits.reverse()
i = 0
for commit in commits:
    i += 1
    print(i,': ', commit.hexsha)

    # to see parents of the commit 
    #print('Parents: ',commit.parents)

从上面的代码,我有以下输出:

Commits in Branches:
master :  13
origin/HEAD :  13
origin/master :  13
origin/new_branch :  8
origin/test-branch :  3
origin/yet_another_branch :  14

Commits in master:
1 :  694df9fee2f9c03a33979725e76a484bce1738a0
2 :  c0fe1b76131b7fcb103f171fd93d85cda17b756c
3 :  0199ad335f65d52a2895a678a19e209e1e16a1a7
4 :  dd0903259b0aadbf2d8fb00e566eee014264f7c0
5 :  7ed55c51e2527f47bc6344cd960ff5beb90cc65d
6 :  d10f19c85fbc1c27b7719a2dc64989255697181d
7 :  c41bdfaeae1f801776420ce161ca2555dffc5aad
8 :  56b5d6e1831a477c79e0fd336acc96ca266d5dea
9 :  6305a72d4e257ebe74b10ca538906f1eceb091bf
10 :  4c5d1ebe5f2f8168ee8bf4a969855821d04caf09
11 :  362bc52be00af3fb917196cf27a8ddc0bb8fd4ba
12 :  5a70a46394eb08b4b48f9eb05798048ca7269a9d
13 :  f4a8bdd318b2678191d06616a55df26416a28363

我想要以下输出。这样就为图中的每个黑点打印“master”,为非黑色提交打印其他分支名称(在这种情况下,对于绿色提交 3,应该打印 test-branch)

Commits in master:
1 :  694df9fee2f9c03a33979725e76a484bce1738a0 master
2 :  c0fe1b76131b7fcb103f171fd93d85cda17b756c master
3 :  0199ad335f65d52a2895a678a19e209e1e16a1a7 test-branch
4 :  dd0903259b0aadbf2d8fb00e566eee014264f7c0 master
5 :  7ed55c51e2527f47bc6344cd960ff5beb90cc65d master
6 :  d10f19c85fbc1c27b7719a2dc64989255697181d master
7 :  c41bdfaeae1f801776420ce161ca2555dffc5aad master
8 :  56b5d6e1831a477c79e0fd336acc96ca266d5dea master
9 :  6305a72d4e257ebe74b10ca538906f1eceb091bf master
10 :  4c5d1ebe5f2f8168ee8bf4a969855821d04caf09 master
11 :  362bc52be00af3fb917196cf27a8ddc0bb8fd4ba master
12 :  5a70a46394eb08b4b48f9eb05798048ca7269a9d master
13 :  f4a8bdd318b2678191d06616a55df26416a28363 master

我需要从提交 1 迭代到 13,并且在此过程中我需要确定哪个提交属于哪个分支。非常感谢。

【问题讨论】:

    标签: python-3.x git gitpython


    【解决方案1】:

    如您所见:

    commit 1,2 被视为所有分支的一部分

    也就是说,来自任何给定分支的可达提交集,由从分支尖端提交开始并通过提交的有向无环图向后工作确定,始终包括提交 1 和 2。

    但我想将所有黑色提交视为主 [分支] 的一部分

    在这种情况下,首先要查找所有提交的图表。您可能知道,图被定义为 G = (V, E),其中 V 是所有顶点的集合,E 是所有边的集合。 Git 将顶点和边数据一起存储在一个提交中:提交的标识是它的哈希 ID,它的边——实际上,因为这是一个有向图——是它的父提交哈希 ID。

    接下来,使用您希望指定为“最重要”分支的名称(即master)来查找其提示提交的哈希 ID。将此提交分配给主集。遍历图表的可到达部分,从这个提交开始,将每个提交添加到master 中的提交集。

    现在,对于每个剩余的分支——按某种顺序,在许多情况下,这个顺序将决定你的结果,所以你可能希望使用拓扑排序——从分支的顶端开始,遍历图的可达部分:

    • 对于已分配给某个分支的任何提交,请忽略它,此时您可以立即停止遍历图表,因为根据定义,它的所有前辈都将分配给某个分支。
    • 您在本次遍历期间达到的提交集是您希望声明“属于”此分支的提交集。

    有多种方法可以实现这一点,包括遍历由集合减法确定的子图:只需从原始 G 中减去每个分支的子图。

    如果它更方便——很可能是这样,因为你不必找到 G——你可以从另一个方向这样做:从 master 开始并找到可访问的提交不在某些最初为空的集合中。将每个提交添加到集合中,同时将它们列为“in master”。然后遍历剩余的分支:如果一个提交在集合中,它已经被声明,否则它被这个分支声明。以这种方式工作的问题是,在选择较小的分支 (develop) 之前,您可能会选择一些包含由其他分支 (develop) 包含的所有提交的分支 (feature-X):您不能进行拓扑不使用完整图进行排序。

    为所有分支提示完成此操作后,您现在已将每个可到达分支提示提交分配给单个分支(而不是像 Git 所做的那样,将其分配给 每个 可以到达的分支)。

    请注意,Git 图中可能存在无法从任何分支提示访问的提交(例如,可以从标记访问但不能从分支访问)。如果您深入研究 Git 的内部结构,您会发现只能从 reflog 条目中访问的提交,甚至一些完全无法访问的提交,只有通过遍历整个对象键值数据库才能发现。后者本质上是 git gc 所做的:遍历数据库以查找所有对象,然后执行标记和清除垃圾收集操作,就像 Lisp 所做的那样,保留可达对象并丢弃不可达对象。

    【讨论】:

    • 我期待,会有一种更简单的方法来找到提交的分支[es]。这似乎相当复杂:(非常感谢您的详细回复。
    • 从根本上说,这很棘手因为提交在多个分支上。根据到达它的分支集列出每个提交通常更有意义,即git branch --contains <commit>的输出。
    • 顺便说一下,您可以在 Python 中引入一些令人印象深刻的完整图形操纵器库。 Stackoverflow 不是问的正确地方,但请查看已关闭问题的答案stackoverflow.com/q/606516/1256452
    【解决方案2】:

    您可能想尝试“--first-parent”选项:

    git log --oneline --first-parent master
    

    从数学上讲,这是一个图,它使得在合并点,没有一个分支应该比另一个“更重要”。但事实上,问题总是会出现,并且在执行“合并”操作时,实际上会将外部分支“带入”当前分支。因此,当前分支被声明为提交对象内的第一个分支。

    如果您在大型项目(如 linux 内核)的主分支上尝试此操作,您将主要落在合并点上,分支上只有几个直接变更集。

    如果这正是您想了解的,您可以另外指定“--no-merges”以明确排除合并点。

    git --oneline --first-parent --no-merges master
    

    例如,这会从您的图表中排除点 47

    最后,要将搜索限制为仅属于特定分支且不从主分支继承的提交,请使用“..”运算符:

    git log master..yourbranch
    

    ... 只会显示可从“yourbranch”访问但不能从“master”访问的提交。

    【讨论】:

    • 感谢提供线索。感觉,用 git 控制台命令解决问题会比用 gitpython 更容易。顺便提一句。 git log master..origin/test-branch 不返回任何提交,因为 test-branch 与 master 合并,因此是它的一部分。但我可以通过简单的集合操作找到不同之处。非常感谢。
    • 确实,最后一个命令应该返回一个空的提交集,因为分支在第 4 步合并,这使得它的所有提交都可以从主分支访问。现在,您可能感兴趣的是使用 git 客户端,无论是基于图形的还是基于文本的(例如 tig),它清楚地实现了分支并使它们更易于浏览。
    猜你喜欢
    • 2022-01-19
    • 1970-01-01
    • 2014-03-23
    • 2022-08-02
    • 1970-01-01
    • 2015-01-12
    • 2020-10-24
    • 2017-03-16
    • 2019-03-16
    相关资源
    最近更新 更多