/** * Returns a list of versions starting from source and ending with target. This method returns the * version always in an ascanding order. So if you need it ordered differently you have to reverse * the list. * * @param projectId project id * @param source source * @param target target * @return list of versions * @throws ESException if source or target are out of range or any other problem occurs */ protected List<Version> getVersions( ProjectId projectId, PrimaryVersionSpec source, PrimaryVersionSpec target) throws ESException { if (source.compareTo(target) < 1) { final ProjectHistory projectHistory = getSubInterface(ProjectSubInterfaceImpl.class).getProject(projectId); final Version sourceVersion = getVersion(projectHistory, source); final Version targetVersion = getVersion(projectHistory, target); if (sourceVersion == null || targetVersion == null) { throw new InvalidVersionSpecException(Messages.VersionSubInterfaceImpl_NoSourceNorTarget); } final List<Version> result = new ArrayList<Version>(); // since the introduction of branches the versions are collected // in different order. Version currentVersion = targetVersion; while (currentVersion != null) { result.add(currentVersion); if (currentVersion.equals(sourceVersion)) { break; } if (currentVersion.getPrimarySpec().compareTo(sourceVersion.getPrimarySpec()) < 0) { // walked too far, invalid path. throw new InvalidVersionSpecException(Messages.VersionSubInterfaceImpl_InvalidPath); } // Shortcut for most common merge usecase: If you have 2 // parallel branches and merge several times // from the one branch into the another. if (currentVersion.getMergedFromVersion().contains(sourceVersion)) { // add sourceVersion because #getChanges always removes // the first version result.add(sourceVersion); break; } currentVersion = findNextVersion(currentVersion); } // versions are collected in descending order, so the result has to be reversed. Collections.reverse(result); return result; } return getVersions(projectId, target, source); }
private PrimaryVersionSpec resolveAncestorVersionSpec( ProjectHistory projectHistory, AncestorVersionSpec versionSpec) throws InvalidVersionSpecException { Version currentSource = getVersion(projectHistory, versionSpec.getSource()); Version currentTarget = getVersion(projectHistory, versionSpec.getTarget()); if (currentSource == null || currentTarget == null) { throw new InvalidVersionSpecException( Messages.VersionSubInterfaceImpl_Invalid_Source_Or_Target); } // The goal is to find the common ancestor version of the source and // target version from different branches. In // order to find the ancestor the algorithm starts at the specified // version and walks down the version tree in // parallel for source and target until the current versions are equal // and the ancestor is found. In Each step // only one version (of target and source) is decremented. To find the // global ancestor it is necessary that the // version with the higher version number is decremented. while (currentSource != null && currentTarget != null) { if (currentSource == currentTarget) { return currentSource.getPrimarySpec(); } // Shortcut for most common merge usecase: If you have 2 parallel // branches, only seperated by one level and merge several times from the one branch into the // another. // This case is also supported by #getVersions if (currentSource.getMergedFromVersion().contains(currentTarget)) { return currentTarget.getPrimarySpec(); } if (currentSource.getPrimarySpec().getIdentifier() >= currentTarget.getPrimarySpec().getIdentifier()) { currentSource = findNextVersion(currentSource); } else { currentTarget = findNextVersion(currentTarget); } } throw new InvalidVersionSpecException(Messages.VersionSubInterfaceImpl_NoAncestorFound); }
private PrimaryVersionSpec internalCreateVersion( ProjectId projectId, PrimaryVersionSpec baseVersionSpec, AbstractChangePackage changePackage, BranchVersionSpec targetBranch, PrimaryVersionSpec sourceVersion, LogMessage logMessage, final ACUser user) throws ESException { synchronized (getMonitor()) { final long currentTimeMillis = System.currentTimeMillis(); final ProjectHistory projectHistory = getSubInterface(ProjectSubInterfaceImpl.class).getProject(projectId); // Find branch final BranchInfo baseBranch = getBranchInfo(projectHistory, baseVersionSpec); final Version baseVersion = getVersion(projectHistory, baseVersionSpec); if (baseVersion == null || baseBranch == null) { throw new InvalidVersionSpecException( Messages.VersionSubInterfaceImpl_InvalidBranchOrVersion); } // defined here fore scoping reasons Version newVersion = null; BranchInfo newBranch = null; // copy project and apply changes final Project newProjectState = ((ProjectImpl) getSubInterface(ProjectSubInterfaceImpl.class).getProject(baseVersion)) .copy(); changePackage.apply(newProjectState); // regular commit if (isRegularCommit(targetBranch, baseVersion)) { newVersion = performRegularCommit( baseVersionSpec, logMessage, user, projectHistory, baseBranch, baseVersion, newProjectState); // case for new branch creation } else if (isNewBranchCommit(targetBranch, projectHistory)) { checkNewBranchCommitPreRequisites(targetBranch.getBranch()); // when branch does NOT exist, create new branch newVersion = createVersion(projectHistory, newProjectState, logMessage, user, baseVersion); newBranch = createNewBranch( projectHistory, baseVersion.getPrimarySpec(), newVersion.getPrimarySpec(), targetBranch); newVersion.setAncestorVersion(baseVersion); } else { // This point only can be reached with invalid input throw new IllegalStateException( Messages.VersionSubInterfaceImpl_TargetBranchCombination_Invalid); } if (sourceVersion != null) { newVersion.getMergedFromVersion().add(getVersion(projectHistory, sourceVersion)); } // try to save try { try { trySave(projectId, changePackage, projectHistory, newVersion, newProjectState); } catch (final FatalESException e) { // try to roll back. removing version is necessary in all cases rollback(projectHistory, baseBranch, baseVersion, newVersion, newBranch, e); } // if ancestor isn't null, a new branch was created. In this // case we want to keep the old base project // state if (newVersion.getAncestorVersion() == null && baseVersion.getProjectState() != null) { // delete projectstate from last revision depending on // persistence policy deleteOldProjectStateAccordingToOptions(projectId, baseVersion); } save(baseVersion); save(projectHistory); } catch (final FatalESException e) { // roll back failed EMFStoreController.getInstance().shutdown(e); throw new ESException(Messages.VersionSubInterfaceImpl_ShuttingServerDown); } ModelUtil.logInfo( Messages.VersionSubInterfaceImpl_TotalTimeForCommit + (System.currentTimeMillis() - currentTimeMillis)); return newVersion.getPrimarySpec(); } }