Search in sources :

Example 16 with Patch

use of io.fabric8.patch.management.Patch 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);
                    }
                }
            }
        }
    }
}
Also used : DirCacheEntry(org.eclipse.jgit.dircache.DirCacheEntry) ConflictResolver(io.fabric8.patch.management.conflicts.ConflictResolver) PropertiesFileResolver(io.fabric8.patch.management.conflicts.PropertiesFileResolver) Resolver(io.fabric8.patch.management.conflicts.Resolver) HashMap(java.util.HashMap) DirCache(org.eclipse.jgit.dircache.DirCache) PropertiesFileResolver(io.fabric8.patch.management.conflicts.PropertiesFileResolver) EOLFixingFileOutputStream(io.fabric8.patch.management.io.EOLFixingFileOutputStream) FileOutputStream(java.io.FileOutputStream) ObjectReader(org.eclipse.jgit.lib.ObjectReader) ObjectLoader(org.eclipse.jgit.lib.ObjectLoader) Map(java.util.Map) TreeMap(java.util.TreeMap) HashMap(java.util.HashMap) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) File(java.io.File)

Example 17 with Patch

use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.

the class GitPatchManagementServiceImpl method mergeProfileChanges.

@Override
public void mergeProfileChanges(Patch patch, File gitRepository, String versionBranch, String patchBranch) {
    Git git = null;
    try {
        git = Git.open(gitRepository);
        // merge version branch with patch branch - no fast forward, so we have nice named commit
        git.checkout().setCreateBranch(false).setName(versionBranch).call();
        MergeResult result = git.merge().setFastForward(MergeCommand.FastForwardMode.NO_FF).include(git.getRepository().resolve(patchBranch)).setCommit(false).call();
        boolean commit = true;
        if (result.getMergeStatus() == MergeResult.MergeStatus.CONFLICTING) {
            handleMergeConflict(git, result, versionBranch, patchBranch);
        } else if (result.getMergeStatus() != MergeResult.MergeStatus.MERGED_NOT_COMMITTED) {
            commit = false;
            Activator.log2(LogService.LOG_ERROR, "Can't merge version branch \"" + versionBranch + "\" with" + " patch branch \"" + patchBranch + "\". Resetting the branch.");
            git.reset().setMode(ResetCommand.ResetType.HARD).call();
        }
    // if (commit) {
    // git.commit()
    // .setMessage("Installing rollup patch \"" + patch.getPatchData().getId() + "\"")
    // .call();
    // }
    } catch (Exception e) {
        throw new PatchException(e.getMessage(), e);
    } finally {
        if (git != null) {
            try {
                git.branchDelete().setBranchNames(patchBranch).setForce(true).call();
            } catch (GitAPIException e) {
                Activator.log2(LogService.LOG_ERROR, e.getMessage());
            }
            gitPatchRepository.closeRepository(git, false);
        }
    }
}
Also used : GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) Git(org.eclipse.jgit.api.Git) MergeResult(org.eclipse.jgit.api.MergeResult) PatchException(io.fabric8.patch.management.PatchException) PatchException(io.fabric8.patch.management.PatchException) GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException)

Example 18 with Patch

use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.

the class GitPatchManagementServiceImpl method beginInstallation.

@Override
public String beginInstallation(PatchKind kind) {
    String tx = null;
    try {
        Git fork = gitPatchRepository.cloneRepository(gitPatchRepository.findOrCreateMainGitRepository(), true);
        Ref installationBranch = null;
        // let's pick up latest user changes
        applyUserChanges(fork);
        switch(kind) {
            case ROLLUP:
                // create temporary branch from the current baseline - rollup patch installation is a rebase
                // of existing user changes on top of new baseline
                RevTag currentBaseline = gitPatchRepository.findCurrentBaseline(fork);
                installationBranch = gitPatchRepository.checkout(fork).setName(String.format("patch-install-%s", GitPatchRepository.TS.format(new Date()))).setCreateBranch(true).setStartPoint(currentBaseline.getTagName() + "^{commit}").call();
                break;
            case NON_ROLLUP:
                // create temporary branch from main-patch-branch/HEAD - non-rollup patch installation is cherry-pick
                // of non-rollup patch commit over existing user changes - we can fast forward when finished
                installationBranch = gitPatchRepository.checkout(fork).setName(String.format("patch-install-%s", GitPatchRepository.TS.format(new Date()))).setCreateBranch(true).setStartPoint(gitPatchRepository.getMainBranchName()).call();
                break;
        }
        pendingTransactionsTypes.put(installationBranch.getName(), kind);
        pendingTransactions.put(installationBranch.getName(), fork);
        return installationBranch.getName();
    } catch (IOException | GitAPIException e) {
        if (tx != null) {
            pendingTransactions.remove(tx);
            pendingTransactionsTypes.remove(tx);
        }
        throw new PatchException(e.getMessage(), e);
    }
}
Also used : GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) Ref(org.eclipse.jgit.lib.Ref) Git(org.eclipse.jgit.api.Git) RevTag(org.eclipse.jgit.revwalk.RevTag) IOException(java.io.IOException) PatchException(io.fabric8.patch.management.PatchException) Date(java.util.Date)

Example 19 with Patch

use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.

the class GitPatchManagementServiceIT method rollbackNonRollupPatchInstallation.

@Test
public void rollbackNonRollupPatchInstallation() throws IOException, GitAPIException {
    freshKarafStandaloneDistro();
    GitPatchRepository repository = patchManagement();
    PatchManagement management = (PatchManagement) pm;
    preparePatchZip("src/test/resources/content/patch1", "target/karaf/patches/source/patch-1.zip", false);
    List<PatchData> patches = management.fetchPatches(new File("target/karaf/patches/source/patch-1.zip").toURI().toURL());
    Patch patch = management.trackPatch(patches.get(0));
    Git fork = repository.cloneRepository(repository.findOrCreateMainGitRepository(), true);
    ObjectId master1 = fork.getRepository().resolve("master");
    String tx = management.beginInstallation(PatchKind.NON_ROLLUP);
    management.install(tx, patch, null);
    management.rollbackInstallation(tx);
    fork.pull().call();
    ObjectId master2 = fork.getRepository().resolve("master");
    assertThat(master1, equalTo(master2));
    assertThat(fork.tagList().call().size(), equalTo(2));
    assertTrue(repository.containsTag(fork, "patch-management"));
    assertTrue(repository.containsTag(fork, "baseline-6.2.0"));
}
Also used : Git(org.eclipse.jgit.api.Git) ObjectId(org.eclipse.jgit.lib.ObjectId) GitPatchRepository(io.fabric8.patch.management.impl.GitPatchRepository) File(java.io.File) Test(org.junit.Test)

Example 20 with Patch

use of io.fabric8.patch.management.Patch in project fabric8 by jboss-fuse.

the class GitPatchManagementServiceIT method rollbackInstalledRollupPatch.

@Test
public void rollbackInstalledRollupPatch() throws IOException, GitAPIException {
    freshKarafStandaloneDistro();
    GitPatchRepository repository = patchManagement();
    PatchManagement management = (PatchManagement) pm;
    preparePatchZip("src/test/resources/content/patch1", "target/karaf/patches/source/patch-1.zip", false);
    preparePatchZip("src/test/resources/content/patch4", "target/karaf/patches/source/patch-4.zip", false);
    List<PatchData> patches = management.fetchPatches(new File("target/karaf/patches/source/patch-1.zip").toURI().toURL());
    Patch patch1 = management.trackPatch(patches.get(0));
    patches = management.fetchPatches(new File("target/karaf/patches/source/patch-4.zip").toURI().toURL());
    Patch patch4 = management.trackPatch(patches.get(0));
    Git fork = repository.cloneRepository(repository.findOrCreateMainGitRepository(), true);
    ObjectId master1 = fork.getRepository().resolve(GitPatchRepository.HISTORY_BRANCH);
    String tx = management.beginInstallation(PatchKind.ROLLUP);
    management.install(tx, patch4, null);
    management.commitInstallation(tx);
    // install P patch to check if rolling back rollup patch will remove P patch's tag
    tx = management.beginInstallation(PatchKind.NON_ROLLUP);
    management.install(tx, patch1, null);
    management.commitInstallation(tx);
    fork = repository.cloneRepository(repository.findOrCreateMainGitRepository(), true);
    assertTrue(repository.containsTag(fork, "patch-my-patch-1"));
    management.rollback(patch4.getPatchData());
    repository.closeRepository(fork, true);
    fork = repository.cloneRepository(repository.findOrCreateMainGitRepository(), true);
    ObjectId master2 = fork.getRepository().resolve(GitPatchRepository.HISTORY_BRANCH);
    assertThat(master1, not(equalTo(master2)));
    assertThat(fork.tagList().call().size(), equalTo(2));
    assertTrue(repository.containsTag(fork, "patch-management"));
    assertTrue(repository.containsTag(fork, "baseline-6.2.0"));
    assertFalse("When rolling back rollup patch, newer P patches' tags should be removed", repository.containsTag(fork, "patch-my-patch-1"));
    assertThat(repository.findCurrentBaseline(fork).getTagName(), equalTo("baseline-6.2.0"));
// TODO: There should be version restored from backed up conflict
// but we've changed the way rolledback R patch handled - we copy entire WC after rollback
// String binStart = FileUtils.readFileToString(new File(karafHome, "bin/start"));
// assertTrue("bin/start should be at previous version",
// binStart.contains("echo \"This is user's change\""));
}
Also used : Git(org.eclipse.jgit.api.Git) ObjectId(org.eclipse.jgit.lib.ObjectId) GitPatchRepository(io.fabric8.patch.management.impl.GitPatchRepository) File(java.io.File) Test(org.junit.Test)

Aggregations

File (java.io.File)54 Test (org.junit.Test)43 Git (org.eclipse.jgit.api.Git)35 PatchException (io.fabric8.patch.management.PatchException)34 Patch (io.fabric8.patch.management.Patch)30 IOException (java.io.IOException)28 GitPatchManagementServiceImpl (io.fabric8.patch.management.impl.GitPatchManagementServiceImpl)27 GitPatchRepository (io.fabric8.patch.management.impl.GitPatchRepository)26 HashMap (java.util.HashMap)18 RevCommit (org.eclipse.jgit.revwalk.RevCommit)18 LinkedList (java.util.LinkedList)17 ZipFile (org.apache.commons.compress.archivers.zip.ZipFile)17 GitAPIException (org.eclipse.jgit.api.errors.GitAPIException)17 ObjectId (org.eclipse.jgit.lib.ObjectId)17 Bundle (org.osgi.framework.Bundle)17 Version (org.osgi.framework.Version)15 PatchResult (io.fabric8.patch.management.PatchResult)13 BundleUpdate (io.fabric8.patch.management.BundleUpdate)11 PatchData (io.fabric8.patch.management.PatchData)11 ArrayList (java.util.ArrayList)11