View Javadoc
1   /*
2    * Copyright (C) 2008, Google Inc. 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.patch;
12  
13  import static org.eclipse.jgit.lib.Constants.encodeASCII;
14  import static org.eclipse.jgit.util.RawParseUtils.match;
15  import static org.eclipse.jgit.util.RawParseUtils.nextLF;
16  
17  import java.nio.charset.Charset;
18  import java.util.ArrayList;
19  import java.util.Arrays;
20  import java.util.List;
21  
22  import org.eclipse.jgit.lib.AbbreviatedObjectId;
23  import org.eclipse.jgit.lib.FileMode;
24  
25  /**
26   * A file in the Git "diff --cc" or "diff --combined" format.
27   * <p>
28   * A combined diff shows an n-way comparison between two or more ancestors and
29   * the final revision. Its primary function is to perform code reviews on a
30   * merge which introduces changes not in any ancestor.
31   */
32  public class CombinedFileHeader extends FileHeader {
33  	private static final byte[] MODE = encodeASCII("mode "); //$NON-NLS-1$
34  
35  	private AbbreviatedObjectId[] oldIds;
36  
37  	private FileMode[] oldModes;
38  
39  	CombinedFileHeader(byte[] b, int offset) {
40  		super(b, offset);
41  	}
42  
43  	/** {@inheritDoc} */
44  	@Override
45  	@SuppressWarnings("unchecked")
46  	public List<? extends CombinedHunkHeader> getHunks() {
47  		return (List<CombinedHunkHeader>) super.getHunks();
48  	}
49  
50  	/**
51  	 * {@inheritDoc}
52  	 * <p>
53  	 *
54  	 * @return number of ancestor revisions mentioned in this diff.
55  	 */
56  	@Override
57  	public int getParentCount() {
58  		return oldIds.length;
59  	}
60  
61  	/**
62  	 * {@inheritDoc}
63  	 * <p>
64  	 * @return get the file mode of the first parent.
65  	 */
66  	@Override
67  	public FileMode getOldMode() {
68  		return getOldMode(0);
69  	}
70  
71  	/**
72  	 * Get the file mode of the nth ancestor
73  	 *
74  	 * @param nthParent
75  	 *            the ancestor to get the mode of
76  	 * @return the mode of the requested ancestor.
77  	 */
78  	public FileMode getOldMode(int nthParent) {
79  		return oldModes[nthParent];
80  	}
81  
82  	/**
83  	 * {@inheritDoc}
84  	 * <p>
85  	 *
86  	 * @return get the object id of the first parent.
87  	 */
88  	@Override
89  	public AbbreviatedObjectId getOldId() {
90  		return getOldId(0);
91  	}
92  
93  	/**
94  	 * Get the ObjectId of the nth ancestor
95  	 *
96  	 * @param nthParent
97  	 *            the ancestor to get the object id of
98  	 * @return the id of the requested ancestor.
99  	 */
100 	public AbbreviatedObjectId getOldId(int nthParent) {
101 		return oldIds[nthParent];
102 	}
103 
104 	/** {@inheritDoc} */
105 	@Override
106 	public String getScriptText(Charset ocs, Charset ncs) {
107 		final Charset[] cs = new Charset[getParentCount() + 1];
108 		Arrays.fill(cs, ocs);
109 		cs[getParentCount()] = ncs;
110 		return getScriptText(cs);
111 	}
112 
113 	/**
114 	 * {@inheritDoc}
115 	 * <p>
116 	 * Convert the patch script for this file into a string.
117 	 */
118 	@Override
119 	public String getScriptText(Charset[] charsetGuess) {
120 		return super.getScriptText(charsetGuess);
121 	}
122 
123 	@Override
124 	int parseGitHeaders(int ptr, int end) {
125 		while (ptr < end) {
126 			final int eol = nextLF(buf, ptr);
127 			if (isHunkHdr(buf, ptr, end) >= 1) {
128 				// First hunk header; break out and parse them later.
129 				break;
130 
131 			} else if (match(buf, ptr, OLD_NAME) >= 0) {
132 				parseOldName(ptr, eol);
133 
134 			} else if (match(buf, ptr, NEW_NAME) >= 0) {
135 				parseNewName(ptr, eol);
136 
137 			} else if (match(buf, ptr, INDEX) >= 0) {
138 				parseIndexLine(ptr + INDEX.length, eol);
139 
140 			} else if (match(buf, ptr, MODE) >= 0) {
141 				parseModeLine(ptr + MODE.length, eol);
142 
143 			} else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
144 				parseNewFileMode(ptr, eol);
145 
146 			} else if (match(buf, ptr, DELETED_FILE_MODE) >= 0) {
147 				parseDeletedFileMode(ptr + DELETED_FILE_MODE.length, eol);
148 
149 			} else {
150 				// Probably an empty patch (stat dirty).
151 				break;
152 			}
153 
154 			ptr = eol;
155 		}
156 		return ptr;
157 	}
158 
159 	/** {@inheritDoc} */
160 	@Override
161 	protected void parseIndexLine(int ptr, int eol) {
162 		// "index $asha1,$bsha1..$csha1"
163 		//
164 		final List<AbbreviatedObjectId> ids = new ArrayList<>();
165 		while (ptr < eol) {
166 			final int comma = nextLF(buf, ptr, ',');
167 			if (eol <= comma)
168 				break;
169 			ids.add(AbbreviatedObjectId.fromString(buf, ptr, comma - 1));
170 			ptr = comma;
171 		}
172 
173 		oldIds = new AbbreviatedObjectId[ids.size() + 1];
174 		ids.toArray(oldIds);
175 		final int dot2 = nextLF(buf, ptr, '.');
176 		oldIds[ids.size()] = AbbreviatedObjectId.fromString(buf, ptr, dot2 - 1);
177 		newId = AbbreviatedObjectId.fromString(buf, dot2 + 1, eol - 1);
178 		oldModes = new FileMode[oldIds.length];
179 	}
180 
181 	/** {@inheritDoc} */
182 	@Override
183 	protected void parseNewFileMode(int ptr, int eol) {
184 		for (int i = 0; i < oldModes.length; i++)
185 			oldModes[i] = FileMode.MISSING;
186 		super.parseNewFileMode(ptr, eol);
187 	}
188 
189 	@Override
190 	HunkHeader newHunkHeader(int offset) {
191 		return new CombinedHunkHeader(this, offset);
192 	}
193 
194 	private void parseModeLine(int ptr, int eol) {
195 		// "mode $amode,$bmode..$cmode"
196 		//
197 		int n = 0;
198 		while (ptr < eol) {
199 			final int comma = nextLF(buf, ptr, ',');
200 			if (eol <= comma)
201 				break;
202 			oldModes[n++] = parseFileMode(ptr, comma);
203 			ptr = comma;
204 		}
205 		final int dot2 = nextLF(buf, ptr, '.');
206 		oldModes[n] = parseFileMode(ptr, dot2);
207 		newMode = parseFileMode(dot2 + 1, eol);
208 	}
209 
210 	private void parseDeletedFileMode(int ptr, int eol) {
211 		// "deleted file mode $amode,$bmode"
212 		//
213 		changeType = ChangeType.DELETE;
214 		int n = 0;
215 		while (ptr < eol) {
216 			final int comma = nextLF(buf, ptr, ',');
217 			if (eol <= comma)
218 				break;
219 			oldModes[n++] = parseFileMode(ptr, comma);
220 			ptr = comma;
221 		}
222 		oldModes[n] = parseFileMode(ptr, eol);
223 		newMode = FileMode.MISSING;
224 	}
225 }