public Changes updateFrom(Archive archive, IO source) throws IOException {
        if (!archive.isUpdating()) {
            throw new IllegalStateException("!archive.isUpdating()");
        }

        Map<String, LinkDigest> otherMap = new HashMap<String, LinkDigest>(source.getFiles());
        for (String name : getKeys(otherMap)) {
            LinkDigest fileDigest = otherMap.get(name);
            if (fileDigest == null) {
                throw new IOException("FileManifest.IO returned a null fileDigest in the getFiles() map.");
            }
            if (fileDigest.isNullDigest()) {
                // DCI: Think.  Way to prevent double read?
                // Fixup missing hashes.
                otherMap.put(name, IOUtil.getFileDigest(source.getFile(name)));
            }
        }

        Changes changes = diff(mNameToFileDigest, otherMap);

        if (changes.isUnmodified()) {
            return changes;
        }

        removeFiles(new ArrayList<String>(changes.mDeleted));
        for (String added : changes.mAdded) {
            putFile(archive, added, source.getFile(added));
        }
        for (String modified : changes.mModified) {
            putFile(archive, modified, source.getFile(modified));
        }

        doPreCommitCleanup();
        return changes;
    }