【问题标题】:How to call git show --first-parent in jgit?如何在 jgit 中调用 git show --first-parent?
【发布时间】:2015-10-09 12:24:16
【问题描述】:

我需要从 master 获取不属于合并类型且不来自其他分支的提交(它们直接放在 master 上)。 git命令是

git log --no-merges --first-parent

如何使用 JGit 做到这一点? 我设法使用

获得不属于合并类型的提交
RevFilter.NO_MERGES

但是我怎样才能得到直接提交到主分支的提交呢?

【问题讨论】:

    标签: java git jgit


    【解决方案1】:

    要获得第一个父母,您可以拨打RevCommit.getParent() 并从那里开始工作。

    不使用RevWalk 访问树的理由是RevWalk 遍历revs 而不遵循层次结构——即,如果你没有阅读提交,它将读取它的父级——这这不是--first-parent 所做的,也是我们想要的。

    所以Rüdiger Herrmann's answer 将无法删除在 (n+1,...)th 分支上所做的提交。它只会删除在这些分支上所做的第一次提交。

    代码

    Repository repo = git.getRepository();
    try (RevWalk walk = new RevWalk(repo)) {
        RevCommit head = walk.parseCommit(repo.getRef(MASTER).getObjectId());
        int count = 0;
        while (head != null) {
            count++;
            RevCommit[] parents = head.getParents();
            if (parents != null && parents.length > 0) {
                head = walk.parseCommit(parents[0]);
            } else {
                head = null;
            }
        }
        return count;
    }
    

    这是从 groovy 转换而来的 source

    注意事项

    1. 调用walk.parseCommit(parents[0]) 是必需的,因为.getParents() 返回的提交不会被完全解析,并且会在自己调用.getParents() 时给出null
    2. 这是为了根据我的另一个 answer 从 git repo 中为 Android 构建脚本 (gradle) 查找版本代码而编写的。 (见source

    【讨论】:

      【解决方案2】:

      您可以将RevWalk 与自定义过滤器一起使用,该过滤器会忽略除第一个父项之外的所有提交。

      class FirstParentFilter extends RevFilter {
        private Set<RevCommit> ignoreCommits = new HashSet<>();
      
        @Override
        public boolean include( RevWalk revWalk, RevCommit commit ) throws IOException {
          if( commit.getParentCount() > 1 ) {
            ignoreCommits.add( commit.getParent( 1 ) );
          }
          boolean include = true;
          if( ignoreCommits.contains( commit ) ) {
            include = false;
            ignoreCommits.remove( commit );
          }
          return include;    
        }
      
        @Override
        public RevFilter clone() {
          return new FirstParentFilter();
        }
      }
      

      简单过滤器假设最多有两个父级,但可以轻松扩展以处理两个以上的父级。

      结合NO_MERGES 过滤器和拓扑排序(父母之前的所有孩子),它应该可以满足您的需求

      revWalk.sort( RevSort.TOPO );
      revWalk.setRevFilter( AndRevFilter.create( new FirstParentFilter(), RevFilter.NO_MERGES ) );
      

      以下是我用来测试过滤器的简单合并场景:

      git.commit().setMessage( "base" ).call();
      String master = git.getRepository().getFullBranch();
      Ref side = git.branchCreate().setName( "side" ).call();
      git.commit().setMessage( "master" ).call();
      git.checkout().setName( side.getName() ).call();
      git.commit().setMessage( "side" ).call();
      git.merge().include( git.getRepository().getRef( master ) ).setFastForward( NO_FF ).setMessage( "merge" ).call();
      
      try( RevWalk revWalk = new RevWalk( git.getRepository() ) ) {
        revWalk.setRevFilter( AndRevFilter.create( new FirstParentFilter(), RevFilter.NO_MERGES ) );
        revWalk.sort( RevSort.TOPO );
        Ref headRef = git.getRepository().getRef( Constants.HEAD );
        RevCommit headCommit = revWalk.parseCommit( headRef.getObjectId() );
        revWalk.markStart( headCommit );
        for( RevCommit revCommit : revWalk ) {
          System.out.println( revCommit.getShortMessage() );
        }
      }
      

      输出

      side
      base
      

      【讨论】:

      • 不幸的是,它没有按预期工作。我收到来自其他合并/拉取请求的提交。
      • 请提供一个简短但完整的示例来说明问题 - 将其附加到此问题或发布新问题。
      • 我想说。例如:给定两个分支:master 和 testing。我在测试分支上做了一些提交,然后我从 master 创建了一个拉取请求以从测试中获取所有修改。在此拉取请求之后,在主分支上,有来自测试分支的提交。我想从 master 获取不来自其他分支的提交。
      • 一个小型的、独立的 sn-p/test 用例,它初始化一个存储库,创建您所要求的(合并)提交,用 RevWalk 列出它们,并断言/显示什么是预期的,什么不是会很好。
      • 实际上 --first-parent 删除了所有父级和作为其父级的提交,直到达到当前提交的共同父级。此代码仅忽略父提交 - 先保存 - 这是不希望的。您可以在更复杂的 repo 上对其进行测试并查看差异。
      猜你喜欢
      • 2016-12-04
      • 2013-11-29
      • 1970-01-01
      • 2012-11-06
      • 2020-12-13
      • 2015-05-19
      • 2013-03-17
      • 1970-01-01
      • 2015-09-28
      相关资源
      最近更新 更多