【问题标题】:Is there a built-in command to get a path from one commit to another?是否有内置命令可以获取从一个提交到另一个提交的路径?
【发布时间】:2020-12-08 03:15:22
【问题描述】:

问题

使用 git,是否有一个内置命令可以给我一系列提交,从一个源提交到目标提交?

更多详情

假设我有一个历史有点复杂的回购:

*   175015a (HEAD -> master) Merge branch 'br1'  # target commit
|\  
| *   bc03512 Merge branch 'br2' into br1
| |\  
| | *   077f584 Merge branch 'br3' into br2
| | |\  
| | | * 923b15a jjj
| | * | cec6734 iii
| | | * c8259a2 hhh
| | |/  
| * | 45cb234 ggg
| | *   b1dd3c7 Merge branch 'br3' into br2
| | |\  
| | | * 48af382 fff
| | * | 778a504 eee                              # source commit
| | | * f918010 ddd
| | |/  
| | * 9706f28 ccc
| |/  
| * 483f665 bbb
|/  
* 795fa22 aaa
* fd4bef2 first commit

出于某种原因,我想检查从提交eee 到当前HEAD 的提交序列。

我的意思是一系列提交,其中:

  • 第一个提交是target 提交(在我的示例中为175015a (master)
  • n+1 线上的每个提交都是n 线上的提交的父级
  • 最后一次提交是source 提交(在我的示例中为778a504 eee

上面的例子:

             # path illustration from the diagram above
175015a      *   175015a (HEAD -> master) Merge branch 'br1'  # target commit
bc03512      \-*   bc03512 Merge branch 'br2' into br1
077f584        \-*   077f584 Merge branch 'br3' into br2
cec6734          *   cec6734 iii
b1dd3c7          *   b1dd3c7 Merge branch 'br3' into br2
778a504          *   778a504 eee                              # source commit

可以接受。

我正在寻找一组选项以传递给 git rev-listgit log 或任何其他 git 命令。


我尝试过的事情

git log 有很多选择,但我没有找到我想要的:

  • 如果我运行git log 778a504..master,它会保留许多不在两次提交之间的提交(例如,会提到提交fffddd

  • 如果我还添加git log --ancestry-path 778a504..master,一些“寄生虫”提交会被删除,但我仍然会得到两个提交之间的所有合并分支(在示例中:我会同时得到hhhjjj一方面,另一方面iii,我希望只有这两组提交中的一个

  • 如果我从 master 开始使用 git log --first-parent 778a504..master :我无法在图中最左边的行之外进行提交(我只会得到 master

  • 有些路径必然会通过合并提交,所以我不想排除它们:也没有--no-merges

注意:建议为重复的答案表示使用--no-merges,也可能使用--first-parent,所以它不适合这个问题。

【问题讨论】:

  • 您使用哪个命令来获取您显示的提交图?
  • git log --oneline --graph
  • 为什么这还不是您问题的答案?我不太明白你在问什么。
  • 我的意思是,git log source..target 有什么问题?
  • ah :您将获得所有合并提交和其间的分叉分支。我想强调一个单一的路径。

标签: git


【解决方案1】:

[update] 结论是没有办法使用内置命令来执行此操作,您目前必须使用一些外部脚本。我会将此答案标记为已接受。


我想看看是否有只使用内置命令和选项的解决方案。

可以写一个脚本来提取路径,这里是一个用perl写的例子:

#!/usr/bin/perl

use strict;
use warnings;

# This script implements a breadth first search, starting from $target,
# following the 'child -> parent' links, until $source is found or the
# complete history of $target is traversed.

my $source = $ARGV[0];
my $target = $ARGV[1];

my $srcsha = `git rev-parse -q --verify "$source"` || die "'$source' is not a valid source";
chomp($srcsha);
my $tgtsha = `git rev-parse -q --verify "$target"` || die "'$target' is not a valid target";
chomp($tgtsha);

# %seen stores the commits seen so far, linked to the child we are interested in
my %seen = ();
$seen{$tgtsha} = "";

# @stack lists the commits to visit
my @stack = ();
push @stack, $tgtsha;


# print_path : print the path from '$target' down to '$source'
sub print_path {
  my ($source) = @_;

  my @path = ();

  my $sha = $source;
  while ($sha) {
    unshift @path, $sha;
    $sha = $seen{$sha};
  }

  print "$_\n" for @path;
}


# main body :
# as long as there is something to scan, go for it,
# if $source is found along the way, print the path and exit with success
# otherwise, end the loop, and exit with failure

while( scalar(@stack) > 0 ) {
  my $sha = shift @stack;

  # extract parent lines from 'git cat-file -p commit'
  my @parents = `git cat-file -p $sha | grep '^parent ' | cut -c8-`;
  
  foreach my $p (@parents) {
    chomp($p);
    # for each parent, if not seen yet :
    #   * store it as a parent of $sha
    #   * put it on the list of commits to explore next
    if (!$seen{$p}) {
      $seen{$p} = $sha;
      push @stack, $p;

      if ($p eq $srcsha) {
        # if we reached the 'source' commit : stop here
        print_path $p;
        exit 0;
      }
    }
  }
}

# no path found
exit 1;

示例用法:

$ ./git-path.pl 778a504 master
175015a5a00bbb9ad2ee4de23254ace4dbc645eb
bc03512e6082badab86a00cd320d89339741bb7b
077f584c10852cbadeef6c48886fd600cab61aa6
cec6734db31a1053b1b71674671512e1fe1592b1
b1dd3c71e42ca421f306640d4f0fdc69a00aa2c7
778a504c718f30d0dc2c72a30c885f10847f46a8

【讨论】:

  • 有一堆图算法可以做你想做的事,但如果你可以采用 any 路径,那么简单的双向广度优先搜索可能是要走的路。 Git 不会为你做这件事。 git log --ancestry-path start..end 将为您提供路径,但可能包含您不想查看的提交。
【解决方案2】:

git loggit rev-list 之后使用-- 可能会得到你想要的:

git log --all -- e.txt
git rev-list --all -- e.txt

应该做的伎俩

注意:这里似乎已经回答了这个答案:Find commits that modify file names matching a pattern in a GIT repository

【讨论】:

  • 我创建了一些虚假的提交,并将文件名放在提交消息中,这可能具有误导性。
猜你喜欢
  • 2011-03-01
  • 2016-11-27
  • 2010-10-27
  • 2011-07-04
  • 2017-12-22
  • 2020-10-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多