String findDiffBetweenTwoRevisions(RevCommit laterCommit, RevCommit earlierCommit) { if (laterCommit == null || earlierCommit == null) { return null; } String output = null; try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { DiffFormatter diffFormatter = new DiffFormatter(out); diffFormatter.setRepository(gitRepo); diffFormatter.format(earlierCommit.getId(), laterCommit.getId()); output = out.toString(); output = StringUtil.stripTillLastOccurrenceOf(output, "+++ b/cruise-config.xml"); } catch (IOException e) { throw new RuntimeException("Error occurred during diff computation. Message: " + e.getMessage()); } return output; }
public static ArrayMemory valueOf(DiffEntry value) { ArrayMemory memory = new ArrayMemory(); memory.refOfIndex("oldId").assign(value.getOldId() == null ? Memory.NULL : valueOf(value.getOldId().toObjectId())); memory.refOfIndex("oldPath").assign(value.getOldPath()); memory.refOfIndex("oldMode").assign(value.getOldMode() == null ? Memory.NULL : StringMemory.valueOf(value.getOldMode().toString())); memory.refOfIndex("newId").assign(value.getNewId() == null ? Memory.NULL : valueOf(value.getNewId().toObjectId())); memory.refOfIndex("newPath").assign(value.getNewPath()); memory.refOfIndex("newMode").assign(value.getNewMode() == null ? Memory.NULL : StringMemory.valueOf(value.getNewMode().toString())); memory.refOfIndex("score").assign(value.getScore()); memory.refOfIndex("changeType").assign(value.getChangeType().name()); return memory; }
/** * Sort the changes in a commit so that changes to node files appear before changes to edge files. This is done so that * node related changes are applied to the FlowGraph before edge related changes. An example where the order matters * is the case when a commit adds a new node n2 as well as adds an edge from an existing node n1 to n2. To ensure that the * addition of edge n1->n2 is successful, node n2 must exist in the graph and so needs to be added first. For deletions, * the order does not matter and ordering the changes in the commit will result in the same FlowGraph state as if the changes * were unordered. In other words, deletion of a node deletes all its incident edges from the FlowGraph. So processing an * edge deletion later results in a no-op. Note that node and edge files do not change depth in case of modifications. * * If there are multiple commits between successive polls to Git, the re-ordering of changes across commits should not * affect the final state of the FlowGraph. This is because, the order of changes for a given file type (i.e. node or edge) * is preserved. */ @Override void processGitConfigChanges() throws GitAPIException, IOException { List<DiffEntry> changes = this.gitRepo.getChanges(); Collections.sort(changes, (o1, o2) -> { Integer o1Depth = (o1.getNewPath() != null) ? (new Path(o1.getNewPath())).depth() : (new Path(o1.getOldPath())).depth(); Integer o2Depth = (o2.getNewPath() != null) ? (new Path(o2.getNewPath())).depth() : (new Path(o2.getOldPath())).depth(); return o1Depth.compareTo(o2Depth); }); processGitConfigChangesHelper(changes); //Decrements the latch count. The countdown latch is initialized to 1. So after the first time the latch is decremented, // the following operation should be a no-op. this.initComplete.countDown(); }
/** * Add an element (i.e., a {@link DataNode}, or a {@link FlowEdge} to * the {@link FlowGraph} for an added, updated or modified node or edge file. * @param change */ @Override public void addChange(DiffEntry change) { Path path = new Path(change.getNewPath()); if (path.depth() == NODE_FILE_DEPTH) { addDataNode(change); } else if (path.depth() == EDGE_FILE_DEPTH) { addFlowEdge(change); } }
/** * Remove an element (i.e. either a {@link DataNode} or a {@link FlowEdge} from the {@link FlowGraph} for * a renamed or deleted {@link DataNode} or {@link FlowEdge} file. * @param change */ @Override public void removeChange(DiffEntry change) { Path path = new Path(change.getOldPath()); if (path.depth() == NODE_FILE_DEPTH) { removeDataNode(change); } else if (path.depth() == EDGE_FILE_DEPTH) { removeFlowEdge(change); } }
/** * A helper method where actual processing of the list of changes since the last refresh of the repository takes place * and the changes applied. * @throws IOException */ void processGitConfigChangesHelper(List<DiffEntry> changes) throws IOException { for (DiffEntry change : changes) { switch (change.getChangeType()) { case ADD: case MODIFY: addChange(change); break; case DELETE: removeChange(change); break; case RENAME: removeChange(change); addChange(change); break; default: throw new RuntimeException("Unsupported change type " + change.getChangeType()); } } // Done processing changes, so checkpoint this.gitRepo.moveCheckpointAndHashesForward(); }
/** {@inheritDoc} */ @Override public <S extends Sequence> EditList diffNonCommon( SequenceComparator<? super S> cmp, S a, S b) { HashedSequencePair<S> p = new HashedSequencePair<>(cmp, a, b); HashedSequenceComparator<S> hc = p.getComparator(); HashedSequence<S> ha = p.getA(); HashedSequence<S> hb = p.getB(); p = null; EditList res = new EditList(); Edit region = new Edit(0, a.size(), 0, b.size()); diffNonCommon(res, hc, ha, hb, region); return res; }
BlameResult(BlameGenerator bg, String path, RawText text) { generator = bg; resultPath = path; resultContents = text; int cnt = text.size(); sourceCommits = new RevCommit[cnt]; sourceAuthors = new PersonIdent[cnt]; sourceCommitters = new PersonIdent[cnt]; sourceLines = new int[cnt]; sourcePaths = new String[cnt]; }
private SimilarityIndex hash(DiffEntry.Side side, DiffEntry ent) throws IOException, TableFullException { SimilarityIndex r = new SimilarityIndex(); r.hash(reader.open(side, ent)); r.sort(); return r; }
Edit findLongestCommonSequence() { if (!scanA()) return null; lcs = new Edit(0, 0); cnt = maxChainLength + 1; for (int bPtr = region.beginB; bPtr < region.endB;) bPtr = tryLongestCommonSequence(bPtr); return hasCommon && maxChainLength < cnt ? null : lcs; }
private HashedSequence<S> wrap(S base) { final int end = base.size(); final int[] hashes = new int[end]; for (int ptr = 0; ptr < end; ptr++) hashes[ptr] = cmp.hash(base, ptr); return new HashedSequence<>(base, hashes); } }
/** * Entrypoint into the algorithm this class is all about. This method triggers that the * differences between A and B are calculated in form of a list of edits. * @param r portion of the sequences to examine. */ private void calculateEdits(Edit r) { middle.initialize(r.beginA, r.endA, r.beginB, r.endB); if (middle.beginA >= middle.endA && middle.beginB >= middle.endB) return; calculateEdits(middle.beginA, middle.endA, middle.beginB, middle.endB); }
/** * Construct a new edit representing the region after cut. * * @param cut * the cut point. The ending A and B points are used as the * starting points of the returned edit. * @return an edit representing the slice of {@code this} edit that occurs * after {@code cut} ends. */ public final Edit after(Edit cut) { return new Edit(cut.endA, endA, cut.endB, endB); }
/** * Get the path associated with this file. * * @param side * which path to obtain. * @return name for this file. */ public String getPath(Side side) { return side == Side.OLD ? getOldPath() : getNewPath(); }
/** * Add a {@link DataNode} to the {@link FlowGraph}. The method uses the {@link FlowGraphConfigurationKeys#DATA_NODE_CLASS} config * to instantiate a {@link DataNode} from the node config file. * @param change */ private void addDataNode(DiffEntry change) { if (checkFilePath(change.getNewPath(), NODE_FILE_DEPTH)) { Path nodeFilePath = new Path(this.repositoryDir, change.getNewPath()); try { Config config = loadNodeFileWithOverrides(nodeFilePath); Class dataNodeClass = Class.forName(ConfigUtils.getString(config, FlowGraphConfigurationKeys.DATA_NODE_CLASS, FlowGraphConfigurationKeys.DEFAULT_DATA_NODE_CLASS)); DataNode dataNode = (DataNode) GobblinConstructorUtils.invokeLongestConstructor(dataNodeClass, config); if (!this.flowGraph.addDataNode(dataNode)) { log.warn("Could not add DataNode {} to FlowGraph; skipping", dataNode.getId()); } else { log.info("Added Datanode {} to FlowGraph", dataNode.getId()); } } catch (Exception e) { log.warn("Could not add DataNode defined in {} due to exception {}", change.getNewPath(), e.getMessage()); } } }
/** * Remove a {@link DataNode} from the {@link FlowGraph}. The method extracts the nodeId of the * {@link DataNode} from the node config file and uses it to delete the associated {@link DataNode}. * @param change */ private void removeDataNode(DiffEntry change) { if (checkFilePath(change.getOldPath(), NODE_FILE_DEPTH)) { Path nodeFilePath = new Path(this.repositoryDir, change.getOldPath()); Config config = getNodeConfigWithOverrides(ConfigFactory.empty(), nodeFilePath); String nodeId = config.getString(FlowGraphConfigurationKeys.DATA_NODE_ID_KEY); if (!this.flowGraph.deleteDataNode(nodeId)) { log.warn("Could not remove DataNode {} from FlowGraph; skipping", nodeId); } else { log.info("Removed DataNode {} from FlowGraph", nodeId); } } }
/** * Add a {@link FlowSpec} for an added, updated, or modified flow config * @param change */ @Override public void addChange(DiffEntry change) { if (checkConfigFilePath(change.getNewPath())) { Path configFilePath = new Path(this.repositoryDir, change.getNewPath()); try { Config flowConfig = loadConfigFileWithFlowNameOverrides(configFilePath); this.flowCatalog.put(FlowSpec.builder() .withConfig(flowConfig) .withVersion(SPEC_VERSION) .withDescription(SPEC_DESCRIPTION) .build()); } catch (IOException e) { log.warn("Could not load config file: " + configFilePath); } } }
/** * Remove a {@link FlowEdge} from the {@link FlowGraph}. The method uses {@link FlowEdgeFactory} * to construct the edgeId of the {@link FlowEdge} from the config file and uses it to delete the associated * {@link FlowEdge}. * @param change */ private void removeFlowEdge(DiffEntry change) { if (checkFilePath(change.getOldPath(), EDGE_FILE_DEPTH)) { Path edgeFilePath = new Path(this.repositoryDir, change.getOldPath()); try { Config config = getEdgeConfigWithOverrides(ConfigFactory.empty(), edgeFilePath); String edgeId = config.getString(FlowGraphConfigurationKeys.FLOW_EDGE_ID_KEY); if (!this.flowGraph.deleteFlowEdge(edgeId)) { log.warn("Could not remove edge {} from FlowGraph; skipping", edgeId); } else { log.info("Removed edge {} from FlowGraph", edgeId); } } catch (Exception e) { log.warn("Could not remove edge defined in {} due to exception {}", edgeFilePath, e.getMessage()); } } }
/** * Add a {@link FlowEdge} to the {@link FlowGraph}. The method uses the {@link FlowEdgeFactory} instance * provided by the {@link FlowGraph} to build a {@link FlowEdge} from the edge config file. * @param change */ private void addFlowEdge(DiffEntry change) { if (checkFilePath(change.getNewPath(), EDGE_FILE_DEPTH)) { Path edgeFilePath = new Path(this.repositoryDir, change.getNewPath()); try { Config edgeConfig = loadEdgeFileWithOverrides(edgeFilePath); List<SpecExecutor> specExecutors = getSpecExecutors(edgeConfig); Class flowEdgeFactoryClass = Class.forName(ConfigUtils.getString(edgeConfig, FlowGraphConfigurationKeys.FLOW_EDGE_FACTORY_CLASS, FlowGraphConfigurationKeys.DEFAULT_FLOW_EDGE_FACTORY_CLASS)); FlowEdgeFactory flowEdgeFactory = (FlowEdgeFactory) GobblinConstructorUtils.invokeLongestConstructor(flowEdgeFactoryClass, edgeConfig); if (flowCatalog.isPresent()) { FlowEdge edge = flowEdgeFactory.createFlowEdge(edgeConfig, flowCatalog.get(), specExecutors); if (!this.flowGraph.addFlowEdge(edge)) { log.warn("Could not add edge {} to FlowGraph; skipping", edge.getId()); } else { log.info("Added edge {} to FlowGraph", edge.getId()); } } else { log.warn("Could not add edge defined in {} to FlowGraph as FlowCatalog is absent", change.getNewPath()); } } catch (Exception e) { log.warn("Could not add edge defined in {} due to exception {}", change.getNewPath(), e.getMessage()); } } }
/** * remove a {@link FlowSpec} for a deleted or renamed flow config * @param change */ @Override public void removeChange(DiffEntry change) { if (checkConfigFilePath(change.getOldPath())) { Path configFilePath = new Path(this.repositoryDir, change.getOldPath()); String flowName = FSSpecStore.getSpecName(configFilePath); String flowGroup = FSSpecStore.getSpecGroup(configFilePath); // build a dummy config to get the proper URI for delete Config dummyConfig = ConfigBuilder.create() .addPrimitive(ConfigurationKeys.FLOW_GROUP_KEY, flowGroup) .addPrimitive(ConfigurationKeys.FLOW_NAME_KEY, flowName) .build(); FlowSpec spec = FlowSpec.builder() .withConfig(dummyConfig) .withVersion(SPEC_VERSION) .withDescription(SPEC_DESCRIPTION) .build(); this.flowCatalog.remove(spec.getUri()); } }