Skip to content

Instantly share code, notes, and snippets.

@carlosmn
Created February 6, 2012 23:47
Show Gist options
  • Save carlosmn/1755969 to your computer and use it in GitHub Desktop.
Save carlosmn/1755969 to your computer and use it in GitHub Desktop.
A libgit2 implementation of git fsck --unreachable
#!/usr/bin/env ruby
require 'rubygems'
require 'rugged'
require 'set'
def objects_in_tree(repo, tree, objs)
tree.each do |e|
objs << e[:oid]
if e[:type] == :tree then
objects_in_tree(repo, repo.lookup(e[:oid]), objs)
end
end
end
def objects_in_commit(repo, cmt, objs)
lobjs = Array.new
lobjs << cmt.tree.oid
objects_in_tree(repo, cmt.tree, lobjs)
objs.merge(lobjs)
end
old = ARGV[0]
new = ARGV[1]
if old == nil or new == nil then
puts "usage: newly-unreachabe.rb <old tip> <new tip>"
exit 1
end
repo = Rugged::Repository.new('.')
walker = Rugged::Walker.new(repo)
walker.push(old)
walker.hide(new)
# If the old commit is in an ancestor of any of the existing branches,
# it's not unrefereced, so we need to hide all of the branch tips
refs = repo.refs(/heads/).map {|ref| ref.resolve.target}
refs.each do |ref|
walker.hide(ref)
end
objects = Set.new
# This generates a list of objects that are referenced by the
# newly-unreferenced commits. Any of these could become
# unreferenced. The unreferenced commits are themselves objects to
# prune, so we add them to the list as well
walker.each do |cmt|
objects << cmt.oid
objects_in_commit(repo, cmt, objects)
end
#objects.uniq!
#puts "Objects to look at:"
#objects.each do |o|
# puts "object #{o}"
#end
# Do another walk. This time we're listing all the objects referenced
# by reachable commits and removing those from the objects array so we
# don't consider them targets for deletion
walker = Rugged::Walker.new(repo)
refs.each do |ref|
walker.push(ref)
end
objs = Set.new
walker.each do |cmt|
objects_in_commit(repo, cmt, objs)
objects -= objs
objs.clear
break if objects.size == 0
end
puts "Unreachable objects:"
objects.each do |o|
puts "object #{o}"
end
@davidlopezre
Copy link

Hey! thanks this is super useful! And sorry to ask you a question 10 years after this was posted 😁 but what does old and new represent?

old = ARGV[0]
new = ARGV[1]

I think I understand that the walker needs to start somewhere but why is there an old and a new?

@carlosmn
Copy link
Author

carlosmn commented Dec 9, 2021

Like the usage string says, this takes the old and new tip of a branch (or any reference, most likely) as it's meant to compute what became unreachable after a particular push. I'm not sure why the description says it's meant to be fsck --unreachable though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment