private void mergeFunctionTags(Address originAddress, Set<FunctionTag> discardTags,
			Set<FunctionTag> keepTags) {

		// Get all the tags in the Result program for the given function.
		Address resultAddress = originToResultTranslator.getAddress(originAddress);
		Function resultFunction = resultListing.getFunctionContaining(resultAddress);
		if (resultFunction == null) {
			return;
		}
		Set<FunctionTag> resultTags = resultFunction.getTags();

		// Now get all tags in Original, and add them to Result function if they haven't already
		// been added.
		Function originalFunction = originListing.getFunctionContaining(originAddress);
		if (originalFunction != null) {
			Collection<FunctionTag> origTags = originalFunction.getTags();
			for (FunctionTag tag : origTags) {
				if (!containsTag(resultTags, tag.getName())) {
					resultFunction.addTag(tag.getName());
				}
			}
		}

		// Now we have to handle the special cases of a list of tags to discard or keep having
		// been supplied. To start, get the list of tags in Result again, since they've changed.
		resultTags = resultListing.getFunctionContaining(resultAddress).getTags();

		// Now discard any tags we've been told to remove.
		if (discardTags != null) {
			Set<String> tagNames = getTagNames(discardTags);
			for (FunctionTag tag : resultTags) {
				if (tagNames.contains(tag.getName())) {
					resultFunction.removeTag(tag.getName());

					// Interesting Case Alert!
					//
					// This merge case will only occur when one user has deleted a tag and the
					// other has decided to add it to a function. So with that in mind, the following
					// situation is possible:
					//
					// If the merge panel is up and the user selects to keep the version that
					// has the tag, THEN subsequently selects the option where the tag was
					// deleted, we have a problem. When the first option was selected the tag
					// would have been added to the Result database in two places: the tag table
					// itself, and the tag mapping table. So if the user then opts for the other
					// option, we have to not only remove it from the mapping table, but also
					// from the tag table. HOWEVER, we should only remove it from the tag table
					// if no other function is using it.
					removeTagIfUnassigned(tag);
				}
			}
		}

		// And add back any tags we've been told to keep.
		if (keepTags != null) {
			Set<String> tagNames = getTagNames(keepTags);
			for (String tagName : tagNames) {
				if (!containsTag(resultTags, tagName)) {
					resultFunction.addTag(tagName);
				}
			}
		}
	}
Combines function tags in Original with Result, making sure that all tags in the
keepTags list are included, while removing any in the discardTags list.

@param originAddress the address
@param discardTags tags to discard from the result
@param keepTags tags to add to the result
Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMerge.java