       (let [tag (str "voom-branch--" branch)]
         (git {:gitdir gitdir} "tag" "-f" tag (str "origin/" branch))))))
 
-(defn tag-all-repos
-  [& {:keys [fetch]}]
+(defn p-repos
+  "Call f once for each repo dir, in parallel. When all calls are
+  done, return nil."
+  [f]
   (->> (all-repos-dirs)
-       (map #(future
-               (when fetch
-                 (fetch-all [%]))
-               (tag-repo-projects %)))
+       (map #(future (f %)))
        doall
        (map deref)
        dorun))

    lein voom [:long-sha] :print
    lein voom :parse <version-str>"
  [project & args]
  (let [symargs (map edn/read-string args)
        [kargs sargs] (split-with keyword? symargs)
        kargset (set kargs)
        long-sha (kargset :long-sha)
        new-project (delay (update-proj-version project long-sha))]
    ;; TODO throw exception if upstream doesn't contain this commit :no-upstream
    (cond
     (:print kargset) (println (:version @new-project))
     (:parse kargset) (prn (ver-parse (first sargs)))
     (:find-box kargset) (prn (find-box))
     (:box-add kargset) (apply box-add project sargs)
     (:retag-all-repos kargset) (time (doall (mapcat (juxt clear-voom-tags tag-repo-projects) (all-repos-dirs))))
     :else (if-let [f (get sub-commands (first sargs))]
             (apply f @new-project (rest sargs))
             (if (and (dirty-wc? (:root @new-project))
                      (not (:insanely-allow-dirty-working-copy kargset)))
               (lmain/abort "Refusing to continue with dirty working copy. (Hint: Run 'git status')")
               (lmain/resolve-and-apply @new-project sargs))))))


;; TODO: Consider revamping these entry points. Separate top-level
;; lein commands?  Separate lein plugins?
(defn ^:no-project-needed voom
  "Usage:
    lein voom [flags] [lein command ...]
