package com.perforce.cvs; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.perforce.common.node.Action; import com.perforce.config.CFG; import com.perforce.config.Config; import com.perforce.config.ConfigException; import com.perforce.cvs.parser.RcsReader; import com.perforce.cvs.parser.RcsSchema; import com.perforce.cvs.parser.rcstypes.RcsObjectAdmin; import com.perforce.cvs.parser.rcstypes.RcsObjectDelta; import com.perforce.cvs.parser.rcstypes.RcsObjectNum; import com.perforce.cvs.parser.rcstypes.RcsObjectNumList; import com.perforce.cvs.parser.rcstypes.RcsObjectTag; import com.perforce.cvs.parser.rcstypes.RcsObjectTagList; public abstract class RcsNavigator { private Logger logger = LoggerFactory.getLogger(RcsNavigator.class); protected abstract void foundBranchEntry(String name, RevisionEntry entry); protected abstract void foundBranchPoint(String name, RevisionEntry entry); private RcsReader rcsRevision; private Map<String, RcsObjectNum> labelsMap; private Map<RcsObjectNum, String> branchMap; protected RcsReader getRcsRevision() { return rcsRevision; } protected Set<Entry<RcsObjectNum, String>> getBranchMap() { return branchMap.entrySet(); } /** * Takes an RCS file; navigates the RCS to find all revisions, then adds * them to the list for sorting. * * @param rcs * @throws Exception */ public void add(RcsReader rcs) throws Exception { rcsRevision = rcs; labelsMap = new HashMap<String, RcsObjectNum>(); branchMap = new HashMap<RcsObjectNum, String>(); if (logger.isDebugEnabled()) { logger.debug("RCS file: " + rcs.getRcsFile().getName()); } // get label and branch names RcsObjectAdmin admin = rcs.getAdmin(); RcsObjectTagList tags = admin.getSymbols(); for (RcsObjectTag tag : tags.getList()) { buildMaps(tag); } // get (default) branch if defined if (rcs.getAdmin().containsKey(RcsSchema.BRANCH)) { RcsObjectNum branch = (RcsObjectNum) admin.get(RcsSchema.BRANCH); String tag = "branch_" + branch; branchMap.put(branch, tag); } // Process HEAD code-line and recurse branches RcsObjectNum head = rcs.getAdmin().getHead(); followCodeLine(head, null); } /** * Check if a branch ID contains '0' and return short branch ID. e.g. * 1.56.0.2 ==> 1.56.2, else leave as-is for label * * @param id * @return */ protected void buildMaps(RcsObjectTag tag) { RcsObjectNum id = tag.getId(); String name = tag.getTag(); if (id.getValues().contains(0)) { List<Integer> branch = new ArrayList<Integer>(id.getValues()); int index = branch.size() - 2; branch.remove(index); RcsObjectNum branchId = new RcsObjectNum(branch); branchMap.put(branchId, name); if (logger.isDebugEnabled()) { logger.debug("\tbranch: '" + name + "' " + branchId.toString()); } } else { labelsMap.put(name, id); if (logger.isDebugEnabled()) { logger.debug("\tlabel: '" + name + "'"); } } } /** * Follow all the revisions in code-line starting with RcsObjectNum 'id' * * @return * * @throws Exception */ protected void followCodeLine(RcsObjectNum id, RevisionEntry parent) throws Exception { do { RcsObjectDelta revision = rcsRevision.getDelta(id); RevisionEntry entry = new RevisionEntry(revision); // set file permission properties for entry entry.setProps(rcsRevision.getProps()); // set binary if 'expand' is set RcsObjectAdmin admin = rcsRevision.getAdmin(); entry.setBinary(admin.getExpand()); // add label, before revision is added by foundBranch[Point|Entry] List<String> labels = getLabels(id); if (!labels.isEmpty()) { for (String l : labels) { String format = formatName(l); entry.addLabel(format); } } // get tmp content name String tmp = (String) Config.get(CFG.CVS_TMPDIR); String basePath = rcsRevision.getPath(); entry.setTmpFile(tmp + "/" + basePath + "/" + id.toString()); // fetch from id RcsObjectNum parentId = (parent != null) ? parent.getId() : null; NodeTarget node = getNodeName(id, parentId); // Process Symbol list for branches for (Entry<RcsObjectNum, String> br : getBranchMap()) { // Look for a match RcsObjectNum brId = getBranchId(br.getKey()); if (brId.equals(id)) { String tag = br.getValue(); if (node != null) { String fromBranch = node.getName(); if (node.isReverse()) { fromBranch = node.getFrom(); } String fromPath = fromBranch + "/" + basePath; entry.setFromPath(fromPath); entry.setReverse(node.isReverse()); if (entry.getState() == Action.REMOVE) { logger.debug("skipping dead source: " + entry); } else { foundBranchPoint(tag, entry); } } } } // if branch, get name and add to list of revisions to sort if (node != null) { String nodeName = node.getName(); String toPath = nodeName + "/" + basePath; // node is a branch if (!nodeName.equals("main") && id.getMinor() == 1) { String fromBranch = getParentName(parentId); String fromPath = fromBranch + "/" + basePath; entry.setFromPath(fromPath); } entry.setPath(toPath); foundBranchEntry(nodeName, entry); } else { if (logger.isTraceEnabled()) { logger.trace("labeled? not adding: " + entry); } } // follow tags off code line [recursive] RcsObjectNumList tagList = revision.getBranches(); if (!tagList.isEmpty()) { for (RcsObjectNum tag : tagList.getList()) { followCodeLine(tag, entry); } } // next... id = revision.getNext(); } while (id != null); } /** * Format label name, by substituting '{symbol}' with the tag symbol. * * @param name * @return */ protected String formatName(String name) { String formatter; try { formatter = (String) Config.get(CFG.CVS_LABEL_FORMAT); } catch (ConfigException e) { return name; } if (!formatter.isEmpty()) { String label = formatter; name = label.replaceAll("\\{symbol\\}", name); // replace all spaces with '_' name = name.replaceAll(" ", "_"); } return name; } /** * Return shortened branch ID e.g. 1.56.2.1 ==> 1.56.2 * * @param id * @return */ protected RcsObjectNum getBranchId(RcsObjectNum id) { List<Integer> branch = new ArrayList<Integer>(id.getValues()); int index = branch.size() - 1; branch.remove(index); return new RcsObjectNum(branch); } private NodeTarget getNodeName(RcsObjectNum id, RcsObjectNum from) { // check for mainline if (from == null) { return new NodeTarget("main", "main", false); } String fromName = getParentName(from); // check for branch RcsObjectNum branchId = getBranchId(id); if (branchMap.containsKey(branchId)) { String name = branchMap.get(branchId); return new NodeTarget(name, fromName, false); } return null; } private List<String> getLabels(RcsObjectNum id) { ArrayList<String> list = new ArrayList<String>(); if (labelsMap.containsValue(id)) { for (Entry<String, RcsObjectNum> e : labelsMap.entrySet()) { if (e.getValue().equals(id)) { list.add(e.getKey()); } } } return list; } protected String getParentName(RcsObjectNum from) { if (from == null) { return null; } RcsObjectNum branchId = getBranchId(from); // check the list of known branches. if (branchMap.containsKey(branchId)) { return branchMap.get(branchId); } // guess it must be from the 'main' return "main"; } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 13876 | Paul Allen | Rename/move file(s) | ||
//guest/paul_allen/p4convert-maven/src/com/perforce/cvs/RcsNavigator.java | |||||
#1 | 13873 | Paul Allen | Branching using p4convert-maven | ||
//guest/perforce_software/p4convert/src/com/perforce/cvs/RcsNavigator.java | |||||
#20 | 13776 | Paul Allen |
CVS: Update to node Action detection. The RCS State is normally set to 'Exp' and 'dead', but this change allows more user defined states. - Move Action class to common.node package. - Modified test case061 to test custom states. |
||
#19 | 12535 | Paul Allen |
CVS/SVN: Substitute spaces with an '_' for label names. - update SVN test case118 to verify substitution. |
||
#18 | 12516 | Paul Allen |
CVS: Label format fix. CVS branches downgraded to labels were missed during formatting. |
||
#17 | 12440 | Paul Allen | CVS: More efficient parsing of RCS files. | ||
#16 | 12319 | Paul Allen |
CVS: Format label names using com.p4convert.cvs.labelFormat. Default is: '{symbol}' the string is replaced by the CVS symbol used for that label. |
||
#15 | 12300 | Paul Allen | CVS: Prevent first entry 'dead' revisions (n*.1) from showing up as branched revisions. | ||
#14 | 12195 | Paul Allen |
CVS - Use the RCS 'expand' field to detect BINARY files. If set this will take precedence over the type map. - Added test case061 |
||
#13 | 12001 | Paul Allen |
CVS: Fix 'dead' 1.1 revisions from showing up as branched revisions. - Updated testcases 35, 50, 51 #review-12002 @rjackson @nmorse |
||
#12 | 11226 | Paul Allen | Tidy imports and unused code. | ||
#11 | 11144 | Paul Allen |
CVS: Bug fix for strange case of a (dead) 1.1 revision. This seems to occur when the source is from another branch. Not a perfect solution as I change this action to an add, but it allows the conversion to complete and no content is lost. |
||
#10 | 11064 | Paul Allen |
CVS: added detection and support for +x revisions - extended testcase 040 to test for exec bits |
||
#9 | 11006 | Paul Allen | CVS: remove experimental code (not required) | ||
#8 | 10984 | Paul Allen |
CVS: Tidy up logging - added revision count. |
||
#7 | 10978 | Paul Allen |
CVS redesign of RCS Revision Navigation. - Disabled reverse RCS tag lookup for the moment. |
||
#6 | 10945 | Paul Allen |
CVS branch of a branch RCS ID was not calculated for branch. - added test case 050 |
||
#5 | 10943 | Paul Allen |
CVS fix extra branches. Clear labelMap and branchMap between adding new revisions. ---------------------------------------------------- 6608.0 B:F - main/tc_crypt_utils/tests/certpriv.pem (UNKNOWN) [pool-1-thread-1] ERROR node.NodeConvert: CASE-SENSITIVITY ISSUE: Cannot branch a deleted revision to a non-existant or deleted revision. Please check case options and platform types. com.p4convert.p4.caseMode = NONE com.p4convert.p4.lowerCase = false [pool-1-thread-1] ERROR process.ProcessChange: Caught exception on exit java.lang.RuntimeException: CASE-SENSITIVITY ISSUE: Cannot branch a deleted revision to a non-existant or deleted revision. Please check case options and platform types. com.p4convert.p4.caseMode = NONE com.p4convert.p4.lowerCase = false at com.perforce.svn.node.NodeConvert.fileAction(NodeConvert.java:179) at com.perforce.svn.node.NodeConvert.action(NodeConvert.java:79) at com.perforce.cvs.process.CvsProcessNode.processFile(CvsProcessNode.java:119) at com.perforce.common.process.ProcessNode.process(ProcessNode.java:42) at com.perforce.cvs.process.CvsProcessChange.addEntry(CvsProcessChange.java:310) at com.perforce.cvs.process.CvsProcessChange.nextEntry(CvsProcessChange.java:268) at com.perforce.cvs.process.CvsProcessChange.processChange(CvsProcessChange.java:105) at com.perforce.common.process.ProcessChange.runSingle(ProcessChange.java:86) at com.perforce.common.process.ProcessChange.call(ProcessChange.java:49) at com.perforce.common.process.ProcessChange.call(ProcessChange.java:19) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) Caught EXIT shutting down ... |
||
#4 | 10912 | Paul Allen |
Back out changelist 10911 - Tmp file look to be in error |
||
#3 | 10911 | Paul Allen |
CVS memory optimisation - Calculate tmp file (has some risk, but saves lots of memory) - String.intern() must use with java 7 or later. |
||
#2 | 10839 | Paul Allen |
Update Import Test framework (CVS/SVN) to support labels. CVS test case 030 update and code cleanup. |
||
#1 | 10728 | Paul Allen |
CVS: new Label feature. Scans the RCS tree counting revisions on a branch. If the branch only has one revision it gets downgraded to a Label. Support added for Import and Convert mode. Activate using: com.p4convert.cvs.labels=true or in java Config.set(CFG.CVS_LABELS, true); (manual testing only -- automated tests will follow this change) |