public function getState($leaves = null, $atEnd = true)
    {
        if (is_null($leaves)) {
            $leaves = $this->getLeafNodes();
        }

        $state = ProjectState::createObject();
        $state->fromDisk(true);
        if (empty($leaves)) {
            return $state;
        }

        // from the leave go up its family tree though its parents and
        // ancestors until we get to the root_node.
        // this way we get the full lineage we need to follow to get to
        // this leaf from root_node to leaf_node
        // we use this lineage to apply migrations in database
        $lineage = [];
        foreach ($leaves as $appName => $appLeaves) {
            // get lineage
            foreach ($appLeaves as $leaf) {
                $lineage_members = $this->getAncestryTree($appName, $leaf);

                foreach ($lineage_members as $l_member => $l_app) {
                    if (empty($lineage[$l_member][$l_app])) {
                        if (!$atEnd && in_array($l_member, $appLeaves)) {
                            continue;
                        }

                        $lineage[$l_member] = $l_app;
                    }
                }
            }
        }

        // use the lineage to update the project state based on the migrations.
        /* @var $migration Migration */
        foreach ($lineage as $member => $lAppName) {
            $migration = $this->nodes[$lAppName][$member];

            $state = $migration->updateState($state);
        }

        return $state;
    }