Advertisement
gb96

Find Common Ancestor

Nov 27th, 2014
629
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 3.94 KB | None | 0 0
  1. package findcommonancestor;
  2.  
  3. import java.util.Arrays;
  4. import java.util.Collections;
  5. import java.util.HashSet;
  6. import java.util.List;
  7. import java.util.Set;
  8.  
  9.  
  10. /**
  11.  * Finds the common ancestor for 2 commits in a Git commit graph.
  12.  *
  13.  * See http://codereview.stackexchange.com/questions/51547/missing-level-of-technical-depth-common-ancestor
  14.  *
  15.  * @author Greg Bowering
  16.  */
  17. public class MyFindCommonAncestor {
  18.  
  19.   public static String findCommmonAncestor(String[] commitHashes,
  20.       String[][] parentHashes, String commitHash1, String commitHash2) {
  21.  
  22.     if (commitHash1.equals(commitHash2)) {
  23.       // efficiently handle edge case, the 2 commits are the same:
  24.       return commitHash1;
  25.     }
  26.  
  27.     /*
  28.      * Flag whether array scan has encountered commits of interest. If there is
  29.      * a long commit history and the commits in question are quite old, we want
  30.      * to skip quickly to the interesting part of the data. Depends on input
  31.      * commitHashes array being sorted in reverse chronological.
  32.      */
  33.     boolean haveSeenCommit1 = false;
  34.     boolean haveSeenCommit2 = false;
  35.  
  36.     /*
  37.      * Commit ancestors can be considered as an upside-down tree rooted at
  38.      * the commit. In a single scan of the input data we perform two breadth-first
  39.      * searches, one starting from each of commitHash1 and commitHash2.
  40.      * The following 2 ancestors sets keep tabs on the current state of the
  41.      * 2 searches.
  42.      * When either commit is found by the other commit's ancestor search,
  43.      * or if the commit at the current input scan cursor appears in both searches, we
  44.      * have found the lowest common ancestor.
  45.      */
  46.     Set<String> commit1Ancestors = Collections.emptySet();
  47.     Set<String> commit2Ancestors = Collections.emptySet();
  48.  
  49.     /*
  50.      * Single scan through input data. Complexity O(n).
  51.      */
  52.     for (int i = 0; i < commitHashes.length; i++) {
  53.       String commitHash = commitHashes[i];
  54.       String[] parents = parentHashes[i];
  55.       List<String> parentsList = parents == null ? null : Arrays
  56.           .asList(parents);
  57.       if (!haveSeenCommit1 && commitHash1.equals(commitHash)) {
  58.         // Encountered commit1
  59.         if (parents != null) {
  60.           commit1Ancestors = new HashSet<>(parentsList);
  61.         }
  62.         haveSeenCommit1 = true;
  63.       }
  64.       if (!haveSeenCommit2 && commitHash2.equals(commitHash)) {
  65.         // Encountered commit2
  66.         if (parents != null) {
  67.           commit2Ancestors = new HashSet<>(parentsList);
  68.         }
  69.         haveSeenCommit2 = true;
  70.       }
  71.       if (!haveSeenCommit1 && !haveSeenCommit2) {
  72.         // haven't encountered any commits of interest yet
  73.         continue;
  74.       }
  75.       if (commit1Ancestors.contains(commitHash2)) {
  76.         // commit2 is an ancestor of commit1:
  77.         return commitHash2;
  78.       }
  79.       if (commit2Ancestors.contains(commitHash1)) {
  80.         // commit1 is an ancestor of commit2:
  81.         return commitHash1;
  82.       }
  83.       if (commit1Ancestors.contains(commitHash)
  84.           && commit2Ancestors.contains(commitHash)) {
  85.         // found lowest common ancestor:
  86.         return commitHash;
  87.       }
  88.       if (parents != null) {
  89.         // replace ancestors with parents and continue:
  90.         if (commit1Ancestors.remove(commitHash)) {
  91.           commit1Ancestors.addAll(parentsList);
  92.         }
  93.         if (commit2Ancestors.remove(commitHash)) {
  94.           commit2Ancestors.addAll(parentsList);
  95.         }
  96.       }
  97.     }
  98.     // scanned through all input without finding common ancestor.
  99.     return null;
  100.   }
  101.  
  102.   public static void main(String[] args) {
  103.  
  104.     String[] commits = { "G", "F", "E", "D", "C", "B", "A" };
  105.     String[][] parents = { { "F", "D" }, { "E" }, { "B" }, { "C" }, { "B" },
  106.         { "A" }, null };
  107.  
  108.     String commit1 = "G";
  109.     String commit2 = "B";
  110.  
  111.     System.out.println("Common Ancestor is "
  112.         + MyFindCommonAncestor.findCommmonAncestor(commits, parents, commit1,
  113.             commit2));
  114.   }
  115.  
  116. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement