View Javadoc
1   /*
2    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.revwalk;
12  
13  import java.io.IOException;
14  
15  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
16  import org.eclipse.jgit.errors.MissingObjectException;
17  
18  /**
19   * Replaces a RevCommit's parents until not colored with REWRITE.
20   * <p>
21   * Before a RevCommit is returned to the caller its parents are updated to
22   * create a dense DAG. Instead of reporting the actual parents as recorded when
23   * the commit was created the returned commit will reflect the next closest
24   * commit that matched the revision walker's filters.
25   * <p>
26   * This generator is the second phase of a path limited revision walk and
27   * assumes it is receiving RevCommits from {@link TreeRevFilter},
28   * after they have been fully buffered by {@link AbstractRevQueue}. The full
29   * buffering is necessary to allow the simple loop used within our own
30   * {@link #rewrite(RevCommit)} to pull completely through a strand of
31   * {@link RevWalk#REWRITE} colored commits and come up with a simplification
32   * that makes the DAG dense. Not fully buffering the commits first would cause
33   * this loop to abort early, due to commits not being parsed and colored
34   * correctly.
35   *
36   * @see TreeRevFilter
37   */
38  class RewriteGenerator extends Generator {
39  	private static final int REWRITE = RevWalk.REWRITE;
40  
41  	/** For {@link #cleanup(RevCommit[])} to remove duplicate parents. */
42  	private static final int DUPLICATE = RevWalk.TEMP_MARK;
43  
44  	private final Generator source;
45  
46  	RewriteGenerator(Generator s) {
47  		super(s.firstParent);
48  		source = s;
49  	}
50  
51  	@Override
52  	void shareFreeList(BlockRevQueue q) {
53  		source.shareFreeList(q);
54  	}
55  
56  	@Override
57  	int outputType() {
58  		return source.outputType() & ~NEEDS_REWRITE;
59  	}
60  
61  	@Override
62  	RevCommit next() throws MissingObjectException,
63  			IncorrectObjectTypeException, IOException {
64  		final RevCommit c = source.next();
65  		if (c == null) {
66  			return null;
67  		}
68  		boolean rewrote = false;
69  		final RevCommit[] pList = c.parents;
70  		final int nParents = pList.length;
71  		for (int i = 0; i < nParents; i++) {
72  			final RevCommit oldp = pList[i];
73  			final RevCommit newp = rewrite(oldp);
74  			if (firstParent) {
75  				if (newp == null) {
76  					c.parents = RevCommit.NO_PARENTS;
77  				} else {
78  					c.parents = new RevCommit[] { newp };
79  				}
80  				return c;
81  			}
82  			if (oldp != newp) {
83  				pList[i] = newp;
84  				rewrote = true;
85  			}
86  		}
87  		if (rewrote) {
88  			c.parents = cleanup(pList);
89  		}
90  		return c;
91  	}
92  
93  	private RevCommit./../../../org/eclipse/jgit/revwalk/RevCommit.html#RevCommit">RevCommit rewrite(RevCommit p) {
94  		for (;;) {
95  			final RevCommit[] pList = p.parents;
96  			if (pList.length > 1) {
97  				// This parent is a merge, so keep it.
98  				//
99  				return p;
100 			}
101 
102 			if ((p.flags & RevWalk.UNINTERESTING) != 0) {
103 				// Retain uninteresting parents. They show where the
104 				// DAG was cut off because it wasn't interesting.
105 				//
106 				return p;
107 			}
108 
109 			if ((p.flags & REWRITE) == 0) {
110 				// This parent was not eligible for rewriting. We
111 				// need to keep it in the DAG.
112 				//
113 				return p;
114 			}
115 
116 			if (pList.length == 0) {
117 				// We can't go back any further, other than to
118 				// just delete the parent entirely.
119 				//
120 				return null;
121 			}
122 
123 			p = pList[0];
124 		}
125 	}
126 
127 	private RevCommit../../../org/eclipse/jgit/revwalk/RevCommit.html#RevCommit">RevCommit[] cleanup(RevCommit[] oldList) {
128 		// Remove any duplicate parents caused due to rewrites (e.g. a merge
129 		// with two sides that both simplified back into the merge base).
130 		// We also may have deleted a parent by marking it null.
131 		//
132 		int newCnt = 0;
133 		for (int o = 0; o < oldList.length; o++) {
134 			final RevCommit p = oldList[o];
135 			if (p == null)
136 				continue;
137 			if ((p.flags & DUPLICATE) != 0) {
138 				oldList[o] = null;
139 				continue;
140 			}
141 			p.flags |= DUPLICATE;
142 			newCnt++;
143 		}
144 
145 		if (newCnt == oldList.length) {
146 			for (RevCommit p : oldList)
147 				p.flags &= ~DUPLICATE;
148 			return oldList;
149 		}
150 
151 		final RevCommit.html#RevCommit">RevCommit[] newList = new RevCommit[newCnt];
152 		newCnt = 0;
153 		for (RevCommit p : oldList) {
154 			if (p != null) {
155 				newList[newCnt++] = p;
156 				p.flags &= ~DUPLICATE;
157 			}
158 		}
159 
160 		return newList;
161 	}
162 }