use of io.fabric8.patch.management.conflicts.PropertiesFileResolver in project fabric8 by jboss-fuse.
the class GitPatchManagementServiceImpl method handleConflict.
private void handleConflict(File patchDirectory, Git fork, boolean preferNew, String cpPrefix, boolean performBackup, String choose, String backup, boolean rollback) throws GitAPIException, IOException {
Map<String, IndexDiff.StageState> conflicts = fork.status().call().getConflictingStageState();
DirCache cache = fork.getRepository().readDirCache();
// path -> [oursObjectId, baseObjectId, theirsObjectId]
Map<String, ObjectId[]> threeWayMerge = new HashMap<>();
// collect conflicts info
for (int i = 0; i < cache.getEntryCount(); i++) {
DirCacheEntry entry = cache.getEntry(i);
if (entry.getStage() == DirCacheEntry.STAGE_0) {
continue;
}
if (!threeWayMerge.containsKey(entry.getPathString())) {
threeWayMerge.put(entry.getPathString(), new ObjectId[] { null, null, null });
}
if (entry.getStage() == DirCacheEntry.STAGE_1) {
// base
threeWayMerge.get(entry.getPathString())[1] = entry.getObjectId();
}
if (entry.getStage() == DirCacheEntry.STAGE_2) {
// ours
threeWayMerge.get(entry.getPathString())[0] = entry.getObjectId();
}
if (entry.getStage() == DirCacheEntry.STAGE_3) {
// theirs
threeWayMerge.get(entry.getPathString())[2] = entry.getObjectId();
}
}
// resolve conflicts
ObjectReader objectReader = fork.getRepository().newObjectReader();
for (Map.Entry<String, ObjectId[]> entry : threeWayMerge.entrySet()) {
if (entry.getKey().equals("patch-info.txt")) {
fork.rm().addFilepattern(entry.getKey()).call();
continue;
}
Resolver resolver = conflictResolver.getResolver(entry.getKey());
// resolved version - either by custom resolved or using automatic algorithm
String resolved = null;
if (resolver != null && entry.getValue()[0] != null && entry.getValue()[2] != null) {
// custom conflict resolution (don't expect DELETED_BY_X kind of conflict, only BOTH_MODIFIED)
String message = String.format(" - %s (%s): %s", entry.getKey(), conflicts.get(entry.getKey()), "Using " + resolver.getClass().getName() + " to resolve the conflict");
Activator.log2(LogService.LOG_INFO, message);
// when doing custom resolution of conflict, we know that both user and patch has changed the file
// in non-mergeable way.
// If there was no resolver, we simply check what to choose by "preferNew" flag
// But because we have custom resolver, we use "preferNew" flag to check which STAGE points to patch'
// version and we select this patch' version of conflicting file as less important file inside
// custom resolver
File base = null, first = null, second = null;
try {
ObjectLoader loader = null;
if (entry.getValue()[1] != null) {
base = new File(fork.getRepository().getWorkTree(), entry.getKey() + ".1");
loader = objectReader.open(entry.getValue()[1]);
try (FileOutputStream fos = new FileOutputStream(base)) {
loader.copyTo(fos);
}
}
// if preferNew == true (P patch) then "first" file (less important) will be file
// provided by patch ("theirs", STAGE_3)
first = new File(fork.getRepository().getWorkTree(), entry.getKey() + ".2");
loader = objectReader.open(entry.getValue()[preferNew ? 2 : 0]);
try (FileOutputStream fos = new FileOutputStream(first)) {
loader.copyTo(fos);
}
// "second", more important file will be user change
second = new File(fork.getRepository().getWorkTree(), entry.getKey() + ".3");
loader = objectReader.open(entry.getValue()[preferNew ? 0 : 2]);
try (FileOutputStream fos = new FileOutputStream(second)) {
loader.copyTo(fos);
}
// resolvers treat patch change as less important - user lines overwrite patch lines
if (resolver instanceof PropertiesFileResolver) {
// TODO: use options from patch:install / patch:fabric-install command
// by default we use a file that comes from patch and we may add property changes
// from user
// in R patch, preferNew == false, because patch comes first
// in P patch, preferNew == true, because patch comes last
// in R patch + fabric mode, preferNew == true, because we *merge* patch branch into version
// branch
boolean useFirstChangeAsBase = true;
if (entry.getKey().startsWith("etc/")) {
// as base
if (rollback) {
useFirstChangeAsBase = true;
} else {
useFirstChangeAsBase = false;
}
}
resolved = ((ResolverEx) resolver).resolve(first, base, second, useFirstChangeAsBase, rollback);
} else {
resolved = resolver.resolve(first, base, second);
}
if (resolved != null) {
FileUtils.write(new File(fork.getRepository().getWorkTree(), entry.getKey()), resolved);
fork.add().addFilepattern(entry.getKey()).call();
}
} finally {
if (base != null) {
base.delete();
}
if (first != null) {
first.delete();
}
if (second != null) {
second.delete();
}
}
}
if (resolved == null) {
// automatic conflict resolution
String message = String.format(" - %s (%s): Choosing %s", entry.getKey(), conflicts.get(entry.getKey()), choose);
ObjectLoader loader = null;
ObjectLoader loaderForBackup = null;
// longer code, but more readable then series of elvis operators (?:)
if (preferNew) {
switch(conflicts.get(entry.getKey())) {
case BOTH_ADDED:
case BOTH_MODIFIED:
loader = objectReader.open(entry.getValue()[2]);
loaderForBackup = objectReader.open(entry.getValue()[0]);
break;
case BOTH_DELETED:
break;
case DELETED_BY_THEM:
// ENTESB-6003: special case: when R patch removes something and we've modified it
// let's preserve our version
message = String.format(" - %s (%s): Keeping custom change", entry.getKey(), conflicts.get(entry.getKey()));
loader = objectReader.open(entry.getValue()[0]);
break;
case DELETED_BY_US:
loader = objectReader.open(entry.getValue()[2]);
break;
}
} else {
switch(conflicts.get(entry.getKey())) {
case BOTH_ADDED:
case BOTH_MODIFIED:
loader = objectReader.open(entry.getValue()[0]);
loaderForBackup = objectReader.open(entry.getValue()[2]);
break;
case DELETED_BY_THEM:
loader = objectReader.open(entry.getValue()[0]);
break;
case BOTH_DELETED:
case DELETED_BY_US:
break;
}
}
Activator.log2(LogService.LOG_WARNING, message);
if (loader != null) {
try (FileOutputStream fos = new FileOutputStream(new File(fork.getRepository().getWorkTree(), entry.getKey()))) {
loader.copyTo(fos);
}
fork.add().addFilepattern(entry.getKey()).call();
} else {
fork.rm().addFilepattern(entry.getKey()).call();
}
if (performBackup) {
// the other entry should be backed up
if (loaderForBackup != null) {
File target = new File(patchDirectory.getParent(), patchDirectory.getName() + ".backup");
if (isStandaloneChild()) {
target = new File(patchDirectory.getParent(), patchDirectory.getName() + "." + System.getProperty("karaf.name") + ".backup");
}
if (cpPrefix != null) {
target = new File(target, cpPrefix);
}
File file = new File(target, entry.getKey());
message = String.format("Backing up %s to \"%s\"", backup, file.getCanonicalPath());
Activator.log2(LogService.LOG_DEBUG, message);
file.getParentFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream(file)) {
loaderForBackup.copyTo(fos);
}
}
}
}
}
}
Aggregations