   (map->Custard {:dir (fs/file path)
                  :repo nil
                  :watcher nil
-                 :refs (atom [])
-                 :uncommitted (atom nil)}))
+                 :states-map (atom {})}))

;;;; The uncommitted state (working directory)

(defn uncommitted-state [dir]
  (let [graph (parse-uncommitted dir)]
    (State. "UNCOMMITTED"
            "UNCOMMITTED"
            :none
            graph
            #(load-file-from-dir dir %))))

;;;; Branch and tag states

(defn ref-states [repo]
  (let [refs (git-ref/load-all repo)]
    (mapv (fn [ref]
            (State. (:name ref)
                    (:name ref)
                    (:type ref)
                    (if (:head ref)
                      (parse-commit repo (:head ref))
                      [])
                    #(load-file-from-commit repo (:head ref) %)))
          refs)))

;;;; Default Custard implementation

(defrecord Custard [dir repo watcher refs uncommitted]
  ICustard
  (states [this]
    (if @uncommitted
      (into [@uncommitted] @refs)
      @refs))

  (state [this id]
    (if (= id "UNCOMMITTED")
      @uncommitted
      (first (filter #(= id (:id %)) (states this)))))

  component/Lifecycle
  (start [this]
    (let [repo (git-repo/load dir)
          bare? (.isBare (.getRepository repo))]
      (when-not bare?
        (reset! uncommitted (uncommitted-state dir)))
      (reset! refs (ref-states repo))
      (-> this
          (assoc :repo repo)
          (assoc :watcher
                 (watch-dir (fn [_]
                              (when-not bare?
                                (reset! uncommitted
                                        (uncommitted-state dir)))
                              (reset! refs (ref-states repo)))
                            dir)))))

  (stop [this]
    (when watcher


(defprotocol ICustard
  (states [this])
  (state [this name]))

;;;; State representation

(defrecord State [name revision type graph load-file-fn]
  IState
  (project [this]
    (first (into []
