@Override protected ScmResult executeAccurevCommand( AccuRevScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters) throws ScmException, AccuRevException { // Do we have a supplied branch. If not we default to the URL stream. ScmBranch branch = (ScmBranch) parameters.getScmVersion(CommandParameter.BRANCH, null); AccuRevVersion branchVersion = repository.getAccuRevVersion(branch); String stream = branchVersion.getBasisStream(); String fromSpec = branchVersion.getTimeSpec(); String toSpec = "highest"; // Versions ScmVersion startVersion = parameters.getScmVersion(CommandParameter.START_SCM_VERSION, null); ScmVersion endVersion = parameters.getScmVersion(CommandParameter.END_SCM_VERSION, null); if (startVersion != null && StringUtils.isNotEmpty(startVersion.getName())) { AccuRevVersion fromVersion = repository.getAccuRevVersion(startVersion); // if no end version supplied then use same basis as startVersion AccuRevVersion toVersion = endVersion == null ? new AccuRevVersion(fromVersion.getBasisStream(), "now") : repository.getAccuRevVersion(endVersion); if (!StringUtils.equals(fromVersion.getBasisStream(), toVersion.getBasisStream())) { throw new AccuRevException( "Not able to provide change log between different streams " + fromVersion + "," + toVersion); } stream = fromVersion.getBasisStream(); fromSpec = fromVersion.getTimeSpec(); toSpec = toVersion.getTimeSpec(); } Date startDate = parameters.getDate(CommandParameter.START_DATE, null); Date endDate = parameters.getDate(CommandParameter.END_DATE, null); int numDays = parameters.getInt(CommandParameter.NUM_DAYS, 0); if (numDays > 0) { if ((startDate != null || endDate != null)) { throw new ScmException("Start or end date cannot be set if num days is set."); } // Last x days. int day = 24 * 60 * 60 * 1000; startDate = new Date(System.currentTimeMillis() - (long) numDays * day); endDate = new Date(System.currentTimeMillis() + day); } if (endDate != null && startDate == null) { throw new ScmException("The end date is set but the start date isn't."); } // Date parameters override transaction ids in versions if (startDate != null) { fromSpec = AccuRevScmProviderRepository.formatTimeSpec(startDate); } else if (fromSpec == null) { fromSpec = "1"; } // Convert the fromSpec to both a date AND a transaction id by looking up // the nearest transaction in the depot. Transaction fromTransaction = getDepotTransaction(repository, stream, fromSpec); long fromTranId = 1; if (fromTransaction != null) { // This tran id is less than or equal to the date/tranid we requested. fromTranId = fromTransaction.getTranId(); if (startDate == null) { startDate = fromTransaction.getWhen(); } } if (endDate != null) { toSpec = AccuRevScmProviderRepository.formatTimeSpec(endDate); } else if (toSpec == null) { toSpec = "highest"; } Transaction toTransaction = getDepotTransaction(repository, stream, toSpec); long toTranId = 1; if (toTransaction != null) { toTranId = toTransaction.getTranId(); if (endDate == null) { endDate = toTransaction.getWhen(); } } startVersion = new ScmRevision(repository.getRevision(stream, fromTranId)); endVersion = new ScmRevision(repository.getRevision(stream, toTranId)); // TODO Split this method in two here. above to convert params to start and end // (stream,tranid,date) and test independantly List<Transaction> streamHistory = Collections.emptyList(); List<Transaction> workspaceHistory = Collections.emptyList(); List<FileDifference> streamDifferences = Collections.emptyList(); StringBuilder errorMessage = new StringBuilder(); AccuRev accurev = repository.getAccuRev(); Stream changelogStream = accurev.showStream(stream); if (changelogStream == null) { errorMessage.append("Unknown accurev stream -").append(stream).append("."); } else { String message = "Changelog on stream " + stream + "(" + changelogStream.getStreamType() + ") from " + fromTranId + " (" + startDate + "), to " + toTranId + " (" + endDate + ")"; if (startDate != null && startDate.after(endDate) || fromTranId >= toTranId) { getLogger().warn("Skipping out of range " + message); } else { getLogger().info(message); // In 4.7.2 and higher we have a diff command that will list all the file differences in a // stream // and thus can be used to detect upstream changes // Unfortunately diff -v -V -t does not work in workspaces. Stream diffStream = changelogStream; if (changelogStream.isWorkspace()) { workspaceHistory = accurev.history( stream, Long.toString(fromTranId + 1), Long.toString(toTranId), 0, false, false); if (workspaceHistory == null) { errorMessage.append( "history on workspace " + stream + " from " + fromTranId + 1 + " to " + toTranId + " failed."); } // do the diff/hist on the basis stream instead. stream = changelogStream.getBasis(); diffStream = accurev.showStream(stream); } if (AccuRevCapability.DIFF_BETWEEN_STREAMS.isSupported(accurev.getClientVersion())) { if (startDate.before(diffStream.getStartDate())) { getLogger().warn("Skipping diff of " + stream + " due to start date out of range"); } else { streamDifferences = accurev.diff(stream, Long.toString(fromTranId), Long.toString(toTranId)); if (streamDifferences == null) { errorMessage.append( "Diff " + stream + "- " + fromTranId + " to " + toTranId + "failed."); } } } // History needs to start from the transaction after our starting transaction streamHistory = accurev.history( stream, Long.toString(fromTranId + 1), Long.toString(toTranId), 0, false, false); if (streamHistory == null) { errorMessage.append( "history on stream " + stream + " from " + fromTranId + 1 + " to " + toTranId + " failed."); } } } String errorString = errorMessage.toString(); if (StringUtils.isBlank(errorString)) { ChangeLogSet changeLog = getChangeLog( changelogStream, streamDifferences, streamHistory, workspaceHistory, startDate, endDate); changeLog.setEndVersion(endVersion); changeLog.setStartVersion(startVersion); return new ChangeLogScmResult(accurev.getCommandLines(), changeLog); } else { return new ChangeLogScmResult( accurev.getCommandLines(), "AccuRev errors: " + errorMessage, accurev.getErrorOutput(), false); } }
private ChangeLogSet getChangeLog( Stream stream, List<FileDifference> streamDifferences, List<Transaction> streamHistory, List<Transaction> workspaceHistory, Date startDate, Date endDate) { // Collect all the "to" versions from the streamDifferences into a Map by element id // If that version is seen in the promote/keep history then we move it from the map // At the end we create a pseudo ChangeSet for any remaining entries in the map as // representing "upstream changes" Map<Long, FileDifference> differencesMap = new HashMap<Long, FileDifference>(); for (FileDifference fileDifference : streamDifferences) { differencesMap.put(fileDifference.getElementId(), fileDifference); } List<Transaction> mergedHistory = new ArrayList<Transaction>(streamHistory); // will never match a version String streamPrefix = "/"; mergedHistory.addAll(workspaceHistory); streamPrefix = stream.getId() + "/"; List<ChangeSet> entries = new ArrayList<ChangeSet>(streamHistory.size()); for (Transaction t : mergedHistory) { if ((startDate != null && t.getWhen().before(startDate)) || (endDate != null && t.getWhen().after(endDate))) { // This is possible if dates and transactions are mixed in the time spec. continue; } // Needed to make Tck test pass against accurev > 4.7.2 - the changelog only expects to deal // with // files. Stream changes and cross links are important entries in the changelog. // However we should only see mkstream once and it is irrelevant given we are interrogating // the history of this stream. if ("mkstream".equals(t.getTranType())) { continue; } Collection<Version> versions = t.getVersions(); List<ChangeFile> files = new ArrayList<ChangeFile>(versions.size()); for (Version v : versions) { // Remove diff representing this promote FileDifference difference = differencesMap.get(v.getElementId()); // TODO: how are defuncts shown in the version history? if (difference != null) { String newVersionSpec = difference.getNewVersionSpec(); if (newVersionSpec != null && newVersionSpec.equals(v.getRealSpec())) { if (getLogger().isDebugEnabled()) { getLogger().debug("Removing difference for " + v); } differencesMap.remove(v.getElementId()); } } // Add this file, unless the virtual version indicates this is the basis stream, and the // real // version came from our workspace stream (ie, this transaction is a promote from the // workspace // to its basis stream, and is therefore NOT a change if (v.getRealSpec().startsWith(streamPrefix) && !v.getVirtualSpec().startsWith(streamPrefix)) { if (getLogger().isDebugEnabled()) { getLogger().debug("Skipping workspace to basis stream promote " + v); } } else { ChangeFile f = new ChangeFile(v.getElementName(), v.getVirtualSpec() + " (" + v.getRealSpec() + ")"); files.add(f); } } if (versions.isEmpty() || !files.isEmpty()) { ChangeSet changeSet = new ChangeSet(t.getWhen(), t.getComment(), t.getAuthor(), files); entries.add(changeSet); } else { if (getLogger().isDebugEnabled()) { getLogger().debug("All versions removed for " + t); } } } // Anything left in the differencesMap represents a change from a higher stream // We don't have details on who or where these came from, but it is important to // detect these for CI tools like Continuum if (!differencesMap.isEmpty()) { List<ChangeFile> upstreamFiles = new ArrayList<ChangeFile>(); for (FileDifference difference : differencesMap.values()) { if (difference.getNewVersionSpec() != null) { upstreamFiles.add( new ChangeFile(difference.getNewFile().getPath(), difference.getNewVersionSpec())); } else { // difference is a deletion upstreamFiles.add(new ChangeFile(difference.getOldFile().getPath(), null)); } } entries.add(new ChangeSet(endDate, "Upstream changes", "various", upstreamFiles)); } return new ChangeLogSet(entries, startDate, endDate); }