Search in sources :

Example 6 with RevertCommand

use of org.eclipse.jgit.api.RevertCommand in project fuse-karaf by jboss-fuse.

the class GitPatchManagementServiceImpl method rollback.

@Override
public void rollback(PatchData patchData) {
    Git fork = null;
    try {
        fork = gitPatchRepository.cloneRepository(gitPatchRepository.findOrCreateMainGitRepository(), true);
        Ref installationBranch = null;
        PatchKind kind = patchData.isRollupPatch() ? PatchKind.ROLLUP : PatchKind.NON_ROLLUP;
        switch(kind) {
            case ROLLUP:
                {
                    Activator.log2(LogService.LOG_INFO, String.format("Rolling back rollup patch \"%s\"", patchData.getId()));
                    // rolling back a rollup patch should rebase (cherry-pick) all user commits done after current baseline
                    // to previous baseline
                    RevTag currentBaseline = gitPatchRepository.findCurrentBaseline(fork);
                    RevCommit c1 = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve(currentBaseline.getTagName() + "^{commit}"));
                    // remember the commit to discover P patch tags installed on top of rolledback baseline
                    RevCommit since = c1;
                    RevCommit c2 = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve("HEAD"));
                    RevCommit to = c2;
                    Iterable<RevCommit> mainChangesSinceRollupPatch = fork.log().addRange(c1, c2).call();
                    List<RevCommit> userChanges = new LinkedList<>();
                    for (RevCommit rc : mainChangesSinceRollupPatch) {
                        if (isUserChangeCommit(rc)) {
                            userChanges.add(rc);
                        }
                    }
                    if (env == EnvType.STANDALONE) {
                        // remove the tag
                        fork.tagDelete().setTags(currentBaseline.getTagName()).call();
                    }
                    // baselines are stacked on each other
                    RevTag previousBaseline = gitPatchRepository.findNthPreviousBaseline(fork, env == EnvType.STANDALONE ? 0 : 1);
                    c1 = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve(previousBaseline.getTagName() + "^{commit}"));
                    // hard reset of main patch branch - to point to other branch, originating from previous baseline
                    fork.reset().setMode(ResetCommand.ResetType.HARD).setRef(previousBaseline.getTagName() + "^{commit}").call();
                    // reapply those user changes that are not conflicting
                    ListIterator<RevCommit> it = userChanges.listIterator(userChanges.size());
                    Status status = fork.status().call();
                    if (!status.isClean()) {
                        // unstage any garbage
                        fork.reset().setMode(ResetCommand.ResetType.MIXED).call();
                        for (String p : status.getModified()) {
                            gitPatchRepository.checkout(fork).addPath(p).call();
                        }
                    }
                    while (it.hasPrevious()) {
                        RevCommit userChange = it.previous();
                        CherryPickResult cpr = fork.cherryPick().include(userChange.getId()).setNoCommit(true).call();
                        // this time prefer user change on top of previous baseline - this change shouldn't be
                        // conflicting, because when rolling back, patch change was preferred over user change
                        handleCherryPickConflict(patchData.getPatchDirectory(), fork, cpr, userChange, true, PatchKind.ROLLUP, null, false, true);
                        // restore backed up content from the reapplied user change
                        String[] commitMessage = userChange.getFullMessage().split("\n\n");
                        if (commitMessage.length > 1) {
                            // we have original commit (that had conflicts) stored in this commit's full message
                            String ref = commitMessage[commitMessage.length - 1];
                            File backupDir = new File(patchesDir, patchData.getId() + ".backup");
                            if (isStandaloneChild()) {
                                backupDir = new File(patchesDir, patchData.getId() + "." + System.getProperty("karaf.name") + ".backup");
                            }
                            backupDir = new File(backupDir, ref);
                            if (backupDir.exists() && backupDir.isDirectory()) {
                                Activator.log2(LogService.LOG_DEBUG, String.format("Restoring content of %s", backupDir.getCanonicalPath()));
                                copyManagedDirectories(backupDir, karafBase, false, false, false);
                            }
                        }
                        gitPatchRepository.prepareCommit(fork, userChange.getFullMessage()).call();
                    }
                    // rollback should not lead to restoration of old patch management features
                    String productVersion = determineVersion(fork.getRepository().getWorkTree());
                    File featuresCfg = new File(fork.getRepository().getWorkTree(), "etc/org.apache.karaf.features.cfg");
                    if (featuresCfg.isFile()) {
                        if (setCurrentPatchManagementVersion(featuresCfg, productVersion)) {
                            // artificial updates to etc/startup.properties
                            String pmNew = String.format("mvn:org.jboss.fuse.modules.patch/patch-management/%s", bundleContext.getBundle().getVersion().toString());
                            String pmOld = String.format("mvn:org.jboss.fuse.modules.patch/patch-management/%s", productVersion);
                            BundleUpdate update = new BundleUpdate(null, null, pmNew, null, pmOld);
                            List<BundleUpdate> patchManagementUpdates = Collections.singletonList(update);
                            updateReferences(fork, "etc/startup.properties", "", Utils.collectLocationUpdates(patchManagementUpdates));
                            fork.add().addFilepattern("etc/org.apache.karaf.features.cfg").addFilepattern("etc/startup.properties").call();
                            gitPatchRepository.prepareCommit(fork, String.format(MARKER_BASELINE_REPLACE_PATCH_FEATURE_PATTERN, productVersion, bundleContext.getBundle().getVersion().toString())).call();
                        }
                    }
                    gitPatchRepository.push(fork);
                    if (env == EnvType.STANDALONE) {
                        // remove remote tag
                        fork.push().setRefSpecs(new RefSpec().setSource(null).setDestination("refs/tags/" + currentBaseline.getTagName())).call();
                    }
                    // remove tags related to non-rollup patches installed between
                    // rolled back baseline and previous HEAD, because rolling back to previous rollup patch
                    // (previous baseline) equal effectively to starting from fresh baseline
                    RevWalk walk = new RevWalk(fork.getRepository());
                    Map<String, RevTag> tags = gitPatchRepository.findTagsBetween(fork, since, to);
                    for (Map.Entry<String, RevTag> entry : tags.entrySet()) {
                        if (entry.getKey().startsWith("patch-")) {
                            fork.tagDelete().setTags(entry.getKey()).call();
                            fork.push().setRefSpecs(new RefSpec().setSource(null).setDestination("refs/tags/" + entry.getKey())).call();
                            // and remove karaf base from tracked patch result, or even remove the result itself
                            String patchId = entry.getKey().substring("patch-".length());
                            Patch patch = loadPatch(new PatchDetailsRequest(patchId));
                            if (patch != null && patch.getResult() != null) {
                                boolean removed = false;
                                for (Iterator<String> iterator = patch.getResult().getKarafBases().iterator(); iterator.hasNext(); ) {
                                    String base = iterator.next();
                                    if (base.contains("|")) {
                                        String[] kb = base.split("\\s*\\|\\s*");
                                        String containerId = kb[0];
                                        if (System.getProperty("karaf.name", "").equals(containerId)) {
                                            iterator.remove();
                                            removed = true;
                                            break;
                                        }
                                    }
                                }
                                if (removed) {
                                    if (patch.getResult().getKarafBases().size() == 0) {
                                        // just remove the result entirely
                                        new File(patch.getPatchData().getPatchLocation(), patchId + ".patch.result").delete();
                                    } else {
                                        patch.getResult().store();
                                    }
                                    if (isStandaloneChild()) {
                                        File file = new File(patch.getPatchData().getPatchLocation(), patchId + "." + System.getProperty("karaf.name") + ".patch.result");
                                        if (file.isFile()) {
                                            file.delete();
                                        }
                                    }
                                }
                            }
                        }
                    }
                    // HEAD of main patch branch after reset and cherry-picks
                    c2 = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve("HEAD"));
                    // applyChanges(fork, c1, c2);
                    applyChanges(fork, false);
                    break;
                }
            case NON_ROLLUP:
                {
                    Activator.log2(LogService.LOG_INFO, String.format("Rolling back non-rollup patch \"%s\"", patchData.getId()));
                    // rolling back a non-rollup patch is a revert of the patch commit and removal of patch tag
                    String patchTagName = String.format("patch-%s", env == EnvType.STANDALONE ? patchData.getId() : patchData.getId() + "-" + gitPatchRepository.getStandaloneChildkarafName());
                    ObjectId oid = fork.getRepository().resolve(patchTagName);
                    if (oid == null) {
                        throw new PatchException(String.format("Can't find installed patch (tag %s is missing)", patchTagName));
                    }
                    RevCommit commit = new RevWalk(fork.getRepository()).parseCommit(oid);
                    RevertCommand revertCommand = fork.revert().include(commit);
                    RevCommit reverted = revertCommand.call();
                    if (reverted == null) {
                        List<String> unmerged = revertCommand.getUnmergedPaths();
                        Activator.log2(LogService.LOG_WARNING, "Problem rolling back patch \"" + patchData.getId() + "\". The following files where updated later:");
                        for (String path : unmerged) {
                            Activator.log2(LogService.LOG_WARNING, " - " + path);
                        }
                        RevWalk walk = new RevWalk(fork.getRepository());
                        RevCommit head = walk.parseCommit(fork.getRepository().resolve("HEAD"));
                        Map<String, RevTag> tags = gitPatchRepository.findTagsBetween(fork, commit, head);
                        List<RevTag> laterPatches = new LinkedList<>();
                        if (tags.size() > 0) {
                            for (Map.Entry<String, RevTag> tag : tags.entrySet()) {
                                if (tag.getKey().startsWith("patch-")) {
                                    laterPatches.add(tag.getValue());
                                }
                            }
                            Activator.log2(LogService.LOG_INFO, "The following patches were installed after \"" + patchData.getId() + "\":");
                            for (RevTag t : laterPatches) {
                                String message = " - " + t.getTagName().substring("patch-".length());
                                RevObject object = walk.peel(t);
                                if (object != null) {
                                    RevCommit c = walk.parseCommit(object.getId());
                                    String date = GitPatchRepository.FULL_DATE.format(new Date(c.getCommitTime() * 1000L));
                                    message += " (" + date + ")";
                                }
                                Activator.log2(LogService.LOG_INFO, message);
                            }
                        }
                        return;
                    }
                    // TODO: should we restore the backup possibly created when instalilng P patch?
                    // remove the tag
                    fork.tagDelete().setTags(patchTagName).call();
                    gitPatchRepository.push(fork);
                    // remove remote tag
                    fork.push().setRefSpecs(new RefSpec().setSource(null).setDestination(String.format("refs/tags/%s", patchTagName))).call();
                    // HEAD of main patch branch after reset and cherry-picks
                    RevCommit c = new RevWalk(fork.getRepository()).parseCommit(fork.getRepository().resolve("HEAD"));
                    applyChanges(fork, c.getParent(0), c);
                    break;
                }
        }
    } catch (IOException | GitAPIException e) {
        throw new PatchException(e.getMessage(), e);
    } finally {
        if (fork != null) {
            gitPatchRepository.closeRepository(fork, true);
        }
    }
}
Also used : PatchKind(org.jboss.fuse.patch.management.PatchKind) PatchDetailsRequest(org.jboss.fuse.patch.management.PatchDetailsRequest) GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) DirCacheEntry(org.eclipse.jgit.dircache.DirCacheEntry) ZipArchiveEntry(org.apache.commons.compress.archivers.zip.ZipArchiveEntry) DiffEntry(org.eclipse.jgit.diff.DiffEntry) CherryPickResult(org.eclipse.jgit.api.CherryPickResult) RefSpec(org.eclipse.jgit.transport.RefSpec) ListIterator(java.util.ListIterator) Iterator(java.util.Iterator) ArrayList(java.util.ArrayList) List(java.util.List) LinkedList(java.util.LinkedList) BundleUpdate(org.jboss.fuse.patch.management.BundleUpdate) RevCommit(org.eclipse.jgit.revwalk.RevCommit) Status(org.eclipse.jgit.api.Status) RevTag(org.eclipse.jgit.revwalk.RevTag) ObjectId(org.eclipse.jgit.lib.ObjectId) RevObject(org.eclipse.jgit.revwalk.RevObject) IOException(java.io.IOException) RevWalk(org.eclipse.jgit.revwalk.RevWalk) ListIterator(java.util.ListIterator) Date(java.util.Date) Ref(org.eclipse.jgit.lib.Ref) Git(org.eclipse.jgit.api.Git) RevertCommand(org.eclipse.jgit.api.RevertCommand) PatchException(org.jboss.fuse.patch.management.PatchException) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) File(java.io.File) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap) HashMap(java.util.HashMap) Patch(org.jboss.fuse.patch.management.Patch) ManagedPatch(org.jboss.fuse.patch.management.ManagedPatch)

Aggregations

RevertCommand (org.eclipse.jgit.api.RevertCommand)6 Git (org.eclipse.jgit.api.Git)4 GitAPIException (org.eclipse.jgit.api.errors.GitAPIException)4 RevCommit (org.eclipse.jgit.revwalk.RevCommit)4 IOException (java.io.IOException)3 RevWalk (org.eclipse.jgit.revwalk.RevWalk)3 File (java.io.File)2 ArrayList (java.util.ArrayList)2 Date (java.util.Date)2 HashMap (java.util.HashMap)2 LinkedList (java.util.LinkedList)2 List (java.util.List)2 ListIterator (java.util.ListIterator)2 Map (java.util.Map)2 ZipArchiveEntry (org.apache.commons.compress.archivers.zip.ZipArchiveEntry)2 ZipFile (org.apache.commons.compress.archivers.zip.ZipFile)2 CherryPickResult (org.eclipse.jgit.api.CherryPickResult)2 Status (org.eclipse.jgit.api.Status)2 DiffEntry (org.eclipse.jgit.diff.DiffEntry)2 DirCacheEntry (org.eclipse.jgit.dircache.DirCacheEntry)2