revlist: ignore all objects reachable from ignored objects

Usually we call revlist.Objects ignoring a set of commits.
This is not enough to ignore everything reachable from such
set, so we first get all objects reachable from the ignored
set and then walk the tree again ignoring that new set.
diff --git a/plumbing/revlist/revlist.go b/plumbing/revlist/revlist.go
index 1bda2fa..f56cf28 100644
--- a/plumbing/revlist/revlist.go
+++ b/plumbing/revlist/revlist.go
@@ -16,7 +16,26 @@
 // the reachable objects from the given objects. Ignore param are object hashes
 // that we want to ignore on the result. All that objects must be accessible
 // from the object storer.
-func Objects(s storer.EncodedObjectStorer, objects, ignore []plumbing.Hash) ([]plumbing.Hash, error) {
+func Objects(
+	s storer.EncodedObjectStorer,
+	objs,
+	ignore []plumbing.Hash,
+) ([]plumbing.Hash, error) {
+	ignore, err := objects(s, ignore, nil, true)
+	if err != nil {
+		return nil, err
+	}
+
+	return objects(s, objs, ignore, false)
+}
+
+func objects(
+	s storer.EncodedObjectStorer,
+	objects,
+	ignore []plumbing.Hash,
+	allowMissingObjects bool,
+) ([]plumbing.Hash, error) {
+
 	seen := hashListToSet(ignore)
 	result := make(map[plumbing.Hash]bool)
 
@@ -29,6 +48,10 @@
 
 	for _, h := range objects {
 		if err := processObject(s, h, seen, ignore, walkerFunc); err != nil {
+			if allowMissingObjects && err == plumbing.ErrObjectNotFound {
+				continue
+			}
+
 			return nil, err
 		}
 	}
@@ -44,6 +67,10 @@
 	ignore []plumbing.Hash,
 	walkerFunc func(h plumbing.Hash),
 ) error {
+	if seen[h] {
+		return nil
+	}
+
 	o, err := s.EncodedObject(plumbing.AnyObject, h)
 	if err != nil {
 		return err