package com.blubi.branchmaster; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import com.blubi.branchmaster.commandline.Git_CommonAncestor; import com.blubi.branchmaster.commandline.Git_CountCommits; import com.blubi.branchmaster.commandline.Git_IsAncestor; import com.blubi.branchmaster.commandline.Git_LogSingle; public class GitTree { private File homedir; private HashMap<String,List<String>> branchToID = new HashMap<String,List<String>>(); private HashMap<String,Integer> gitNodeIds = new HashMap<String,Integer>(); private List<NodeContainer> gitNodes = new ArrayList<NodeContainer>(); private boolean[][] ancestors; private boolean[][] adjancency; private int[][] commitCount; private class NodeContainer { public String id = ""; public String date = ""; public boolean innerNode = false; public NodeContainer(String id, boolean innerNode, String date) { this.id=id; this.innerNode=innerNode; this.date = date.substring(0,10); } } public GitTree(int branches, File homedir) { this.homedir = homedir; int size = branches*2; ancestors = new boolean[size][size]; commitCount = new int[size][size]; } public String buildTree(String[] branches) { String [] uniq_branches = findIDs(branches); addCommonAncestors(uniq_branches); fillGraph(); return jsonResult(); } private String[] findIDs(String[] branches) { for(int i=0;i<branches.length;i++) { Git_LogSingle.Container data = new Git_LogSingle(this.homedir).execute(branches[i]); List<String> list = branchToID.get(data.id); if(list==null) { list = new ArrayList<String>(); branchToID.put(data.id, list); } list.add(branches[i]); } return branchToID.keySet().toArray(new String[0]); } private void addCommonAncestors(String[] branches) { for(int i=0;i<branches.length;i++) for(int j=i+1;j<branches.length;j++) { String b1 = branches[i]; String b2 = branches[j]; String ancId = new Git_CommonAncestor(this.homedir).execute(b1,b2); if(ancId !=null) addAncestorNode(b1, b2, ancId); } } private void addAncestorNode(String branch1, String branch2, String ancestorNode) { int b1_id = idFromGitNode(branch1, false); int b2_id = idFromGitNode(branch2, false); int anc_id = idFromGitNode(ancestorNode, true); System.out.println(b1_id+";" +b2_id+";"+anc_id); if(b1_id==anc_id){ // b1 IS the ancestor ancestors[anc_id][b2_id] = true; } else if (b2_id==anc_id){ // b2 IS the ancestor ancestors[anc_id][b1_id] = true; } else { // the ancestor is not b1 og b2 ancestors[anc_id][b1_id] = true; ancestors[anc_id][b2_id] = true; } } private int idFromGitNode(String node, boolean innerNode) { Integer id = gitNodeIds.get(node); if(id==null) { id = gitNodes.size(); gitNodeIds.put(node, id); String date = getBranchpointDate(node); gitNodes.add(new NodeContainer(node, innerNode, date)); } return id; } private void fillGraph() { int size = gitNodes.size(); for(int i=0;i<size;i++) for(int j=0;j<size;j++) { if(i==j) continue; if(ancestors[i][j]==false) { ancestors[i][j]=new Git_IsAncestor(this.homedir).execute(gitNodes.get(i).id, gitNodes.get(j).id); } } adjancency = ancestorMatrixToAdjacencyMatrix(ancestors); for(int i=0;i<size;i++) for(int j=0;j<size;j++) if(adjancency[i][j]) { commitCount[i][j] = new Git_CountCommits(this.homedir).execute(gitNodes.get(i).id, gitNodes.get(j).id); } } // Iterative thinning of ancestor matrix to create a minimum edge graph // Algorithm from: http://cs.stackexchange.com/questions/23408/reconstruct-directed-graph-from-list-of-ancestors-for-each-node // private boolean[][] ancestorMatrixToAdjacencyMatrix(boolean[][] ancestors) { int size = ancestors.length; boolean[][] adjancency = new boolean[size][size]; // Initial clone for(int i=0;i<size;i++) for(int j=0;j<size;j++) adjancency[i][j] = ancestors[i][j]; // If there exist an indirect path, eliminate the direct path for(int k=0;k<size;k++) for(int l=0;l<size;l++) for(int m=0;m<size;m++) { if(ancestors[k][l] && ancestors[l][m] && ancestors[k][m]) adjancency[k][m] = false; } return adjancency; } private HashMap<String,String> branchpoint_to_Date = new HashMap<String,String>(); private String getBranchpointDate(String id) { String result = branchpoint_to_Date.get(id); if(result==null) { Git_LogSingle.Container gitlog = new Git_LogSingle(this.homedir).execute(id); branchpoint_to_Date.put(id, gitlog.commit_date); result = gitlog.commit_date; } return result; } private String jsonResult() { int size = gitNodes.size(); JSONObject obj = new JSONObject(); JSONArray nodes = new JSONArray(); for(int i=0;i<size;i++) { JSONObject node = new JSONObject(); node.put("id", i); node.put("date", gitNodes.get(i).date); node.put("inner", gitNodes.get(i).innerNode); List<String> list = branchToID.get(gitNodes.get(i).id); if(list!=null) { JSONArray names = new JSONArray(); for(String s:list) { names.add(s); } node.put("names", names); } String id = gitNodes.get(i).id; String id_short = (gitNodes.get(i).innerNode?id.substring(0,10):id.substring(0,10)); node.put("name", id_short); nodes.add(node); } obj.put("nodes", nodes); JSONArray links = new JSONArray(); for(int i=0;i<size;i++) { for(int j=0;j<size;j++) { if(commitCount[i][j]==0) continue; JSONObject link = new JSONObject(); link.put("source", i); link.put("target", j); link.put("commits", commitCount[i][j]); links.add(link); } } obj.put("links", links); return obj.toString(); } public void debugoutput(){ System.out.println("\n-- Nodes --"); int size = gitNodes.size(); int count=0; for(NodeContainer gn : gitNodes) { List<String> list = branchToID.get(gn.id); String b=""; if(list!=null) for(String s:list) { b += s; } System.out.println((count++)+":"+gn.id+" - "+b); } System.out.println("-- Ancestor --"); for(int i=0;i<size;i++) { String line = gitNodes.get(i).id+" : "; for(int j=0;j<size;j++) { line += (ancestors[i][j]?"1-":"0-"); } System.out.println(line); } System.out.println("-- Adjancency --"); for(int i=0;i<size;i++) { String line = gitNodes.get(i).id+" : "; for(int j=0;j<size;j++) { line += (adjancency[i][j]?"1-":"0-"); } System.out.println(line); } System.out.println("-- Commit count --"); for(int i=0;i<size;i++) { String line = gitNodes.get(i).id+" : "; for(int j=0;j<size;j++) { line += commitCount[i][j]+"-"; } System.out.println(line); } } }