public String commitAndPushToFreenet(PrintStream out) throws IOException {
        FileManifest.Changes changes = mFileManifest.diffTo(mArchive, mOverlay);
        if (changes.isUnmodified()) {
            throw new IOException("Didn't find any local changes to submit.");
        }

        // Copy so that we can cleanly role back if an exception is raised.
        Archive copy = mArchive.deepCopy();
        FileManifest files = FileManifest.fromArchiveRootObject(copy);

        // Commit local changes to the Archive.
        copy.unsetRootObject(RootObjectKind.PARENT_REFERENCES);
        copy.unsetRootObject(RootObjectKind.REBASE_REFERENCES);

        // Update the archive
        copy.startUpdate();
        files.updateFrom(copy, mOverlay);

        LinkDigest digest = copy.updateRootObject(files.toBytes(), RootObjectKind.FILE_MANIFEST);

        if (mParentUri != null) {
            out.println("Set PARENT_REFERENCES: " + mParentUri);
            List<String> keys = Arrays.asList(mParentUri);
            LinkDigest refs =
                copy.updateRootObject(ExternalRefs.create(keys, ExternalRefs.KIND_FREENET)
                                      .toBytes(),
                                      RootObjectKind.PARENT_REFERENCES);
        }

        if (mSecondaryUri != null) {
            out.println("Set REBASE_REFERENCES: " + mSecondaryUri);
            List<String> keys = Arrays.asList(mSecondaryUri);
            LinkDigest refs =
                copy.updateRootObject(ExternalRefs.create(keys, ExternalRefs.KIND_FREENET)
                                      .toBytes(),
                                      RootObjectKind.REBASE_REFERENCES);
        }

        copy.commitUpdate();
        copy.compressAndUpdateArchiveManifest();

        // Generate a unique SSK based on the SHA hash of the archive manifest.
        String insertUri = getInsertUri(copy);

        //out.println("Insert URI: " + insertUri);

        // Push the updated version into Freenet.
        FreenetIO io = makeIO();
        io.setInsertUri(insertUri);
        out.println("Writing to Freenet...");
        copy.write(io);

        if (mFmsId != null) {
            try {
                out.println("Sending FMS update notification to: " + mFmsGroup);
                FMSUtil.sendBISSMsg(mFmsHost, mFmsPort, mFmsId, mFmsGroup,
                                    mBissName, io.getRequestUri());
            } catch (IOException ioe) {
                out.println("FMS send failed: " + ioe.getMessage());
            }
        }

        // Don't update any state until all calls which could raise have finished.
        mArchive = copy;
        mFileManifest = FileManifest.fromArchiveRootObject(mArchive);
        mOverlay = new LocalWikiChanges(mArchive, mFileManifest);
        mParentUri = io.getRequestUri();
        out.println("Request URI: " + mParentUri);

        // Submitting resets secondary archive.
        // DCI: test bugfix
        resetSecondaryArchive(); // Fixes bug present until: b06c3161ca35

        updateLoadedWikiInfo();

        return mParentUri;
    }