void checkin(CheckinHandler checkinHandler, TaskMonitor monitor)
			throws IOException, VersionException, CancelledException {

		if (!versionedFileSystem.isOnline()) {
			throw new NotConnectedException("Not connected to repository server");
		}
		if (fileSystem.isReadOnly() || versionedFileSystem.isReadOnly()) {
			throw new ReadOnlyException(
				"checkin permitted within writeable project and repository only");
		}
		if (!isCheckedOut()) {
			throw new IOException("File not checked out");
		}
		if (isChanged()) {
			throw new IOException("File has unsaved changes");
		}
		if (canRecover()) {
			throw new IOException("File recovery data exists");
		}
		if (!modifiedSinceCheckout()) {
			throw new IOException("File has not been modified since checkout");
		}
		verifyCheckout("checkin");
		if (monitor == null) {
			monitor = TaskMonitor.DUMMY;
		}

		if (busy.getAndSet(true)) {
			throw new FileInUseException(name + " is busy");
		}

		DomainObjectAdapterDB inUseDomainObj = null;
		mergeInProgress = true;
		projectData.mergeStarted();
		try {
			ContentHandler<?> contentHandler = getContentHandler();

			inUseDomainObj = getAndLockInUseDomainObjectForMergeUpdate("checkin");

			boolean keepCheckedOut = checkinHandler.keepCheckedOut();

			if (inUseDomainObj != null && !keepCheckedOut) {
				keepCheckedOut = true;
				Msg.warn(this, "File currently open - must keep checked-out: " + name);
			}

			boolean quickCheckin = ALWAYS_MERGE ? false : quickCheckin(checkinHandler, monitor);

			if (!quickCheckin) {

				if (SystemUtilities.isInHeadlessMode()) {
					throw new IOException(
						"Checkin failed, file requires merge which is not supported in headless mode");
				}

				Msg.info(this, "Checkin with merge for " + name);

				DomainObjectAdapter checkinObj = contentHandler.getDomainObject(versionedFolderItem,
					null, folderItem.getCheckoutId(), false, false, this, monitor);
				checkinObj.setDomainFile(new DomainFileProxy(name, getParent().getPathname(),
					checkinObj, versionedFolderItem.getCurrentVersion() + 1, fileID,
					parent.getProjectLocator()));

				DomainObject sourceObj = null;
				DomainObject originalObj = null;
				DomainObject latestObj = null;
				try {
					synchronized (fileSystem) {
						int coVer = folderItem.getCheckoutVersion();
						sourceObj = contentHandler.getImmutableObject(folderItem, this,
							DomainFile.DEFAULT_VERSION, -1, monitor);
						originalObj = contentHandler.getImmutableObject(versionedFolderItem, this,
							coVer, -1, monitor);
						latestObj = contentHandler.getImmutableObject(versionedFolderItem, this,
							DomainFile.DEFAULT_VERSION, coVer, monitor);
					}
					DomainObjectMergeManager mergeMgr = contentHandler.getMergeManager(checkinObj,
						sourceObj, originalObj, latestObj);

					if (!mergeMgr.merge(monitor)) {
						Msg.info(this, "Checkin with merge terminated for " + name);
						return; // error displayed by merge manager
					}

					checkinObj.save(checkinHandler.getComment(), monitor);

					if (checkinHandler.createKeepFile()) {
						if (monitor != null) {
							monitor.setMessage("Generating local keep file...");
						}
						createKeepFile(sourceObj, monitor);
					}

				}
				finally {
					checkinObj.release(this);
					release(sourceObj);
					release(originalObj);
					release(latestObj);
				}
			}

			synchronized (fileSystem) {

				versionedFolderItem = versionedFileSystem.getItem(parent.getPathname(), name);
				if (versionedFolderItem == null) {
					throw new IOException("Checkin failed, versioned item not found");
				}

				Msg.info(this, "Checkin completed for " + name);

				if (keepCheckedOut) {
					boolean success = false;
					try {
						if (monitor != null) {
							monitor.setMessage("Updating local checkout file...");
						}
						folderItem.updateCheckout(versionedFolderItem, !quickCheckin, monitor);
						versionedFolderItem.updateCheckoutVersion(folderItem.getCheckoutId(),
							folderItem.getCheckoutVersion(), ClientUtil.getUserName());
						success = true;
					}
					finally {
						if (!success) {
							// Failed to update checkout for unknown reason
							try {
								if (inUseDomainObj != null) {
									// On error disassociate open domain object from this file
									projectData.clearDomainObject(getPathname());
									// An invalid version (-2) is specified to avoid file match
									inUseDomainObj.setDomainFile(new DomainFileProxy(name,
										parent.getPathname(), inUseDomainObj, -2, fileID,
										parent.getProjectLocator()));
									inUseDomainObj.setTemporary(true);
								}
								undoCheckout(false, true);
							}
							catch (IOException e) {
								Msg.error(this, "Undo checkout error", e);
							}
						}
					}

					if (inUseDomainObj != null) {
						// Reset source file and change-sets for open database
						contentHandler.resetDBSourceFile(folderItem, inUseDomainObj);
					}
				}
				else {
					undoCheckout(false, true);
				}

			} // end of synchronized block

			if (inUseDomainObj != null) {
				inUseDomainObj.domainObjectRestored();
			}
		}
		finally {
			unlockDomainObject(inUseDomainObj);
			busy.set(false);
			mergeInProgress = false;
			projectData.mergeEnded();
			parent.deleteLocalFolderIfEmpty();
			parent.fileChanged(name);
		}
	}
Performs check in to associated repository.  File must be checked-out 
and modified since checkout.
@param checkinHandler provides user input data to complete checkin process.
@param monitor the TaskMonitor.
@throws IOException if an IO or access error occurs
@throws VersionException if unable to handle domain object version in versioned filesystem.
We are unable to upgrade since this would only occur if checkout is not exclusive.
@throws CancelledException if task monitor cancelled operation
Ghidra/Framework/Project/src/main/java/ghidra/framework/data/GhidraFileData.java