Search code examples
rubygitlibgit2ruggedgit-cherry

Is there a 'git cherry' in libgit2


I'm working on a project and one of the requirements is the need to call git cherry command to get some differences between on branch and the other - (note this is not git cherry-pick).

I was planning to use ruby rugged gem for this project but I cannot find anything in the documentation whether it supports the git cherry API or not.

Rugged is a ruby wrapper around libgit2 but I could not find anything in its documentation either.


Solution

  • libgit2 (and by extension, the things built on top of it, like Rugged) is pretty low level. There probably won't ever be a git cherry-like functionality built in, but you could normally write one yourself.

    Unfortunately, at the moment, libgit2 doesn't provide a patch ID implementation, which is the technique that Git uses to determine identical patches. If it did, or if you wanted to write your own, you could perform the revision walk yourself over the two ranges using Rugged::Walker, comparing which patch IDs from the second range were also present in the first range. That's essentially what Git does under the hood.

    The following is an example of what you might do, replacing the patch_id function with a real one:

    require 'rugged'
    require 'digest'
    
    def patch_id(patch)
      # This is not a real implementation; replace it with a real one.
      Digest::SHA1.hexdigest(patch.each_line.reject { |l| l =~ /^(diff|index|---|\+\+\+|@@)/ }.join)
    end
    
    def walk_revisions(repo, from, to)
      revisions = {}
      walker = Rugged::Walker.new(repo)
      walker.sorting(Rugged::SORT_TOPO | Rugged::SORT_REVERSE)
      walker.push(to)
      walker.hide(from)
      walker.each do |c|
        diff = c.parents[0].diff(c)
        revisions[patch_id(diff.patch)] = c.oid
      end
      revisions
    end
    
    repo = Rugged::Repository.new(ARGV[0])
    a, b, c = ARGV[1..3].map { |r| repo.rev_parse(r) }
    first = walk_revisions(repo, b, a)
    second = walk_revisions(repo, c, b)
    second.each do |id, rev|
      char = first.include?(id) ? '-' : '+'
      puts "#{char} #{rev}"
    end