View Javadoc
1   /*
2    * Copyright (C) 2008, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.patch;
45  
46  import static org.eclipse.jgit.lib.Constants.encodeASCII;
47  import static org.eclipse.jgit.util.RawParseUtils.match;
48  import static org.eclipse.jgit.util.RawParseUtils.nextLF;
49  
50  import java.nio.charset.Charset;
51  import java.util.ArrayList;
52  import java.util.Arrays;
53  import java.util.List;
54  
55  import org.eclipse.jgit.lib.AbbreviatedObjectId;
56  import org.eclipse.jgit.lib.FileMode;
57  
58  /**
59   * A file in the Git "diff --cc" or "diff --combined" format.
60   * <p>
61   * A combined diff shows an n-way comparison between two or more ancestors and
62   * the final revision. Its primary function is to perform code reviews on a
63   * merge which introduces changes not in any ancestor.
64   */
65  public class CombinedFileHeader extends FileHeader {
66  	private static final byte[] MODE = encodeASCII("mode "); //$NON-NLS-1$
67  
68  	private AbbreviatedObjectId[] oldIds;
69  
70  	private FileMode[] oldModes;
71  
72  	CombinedFileHeader(final byte[] b, final int offset) {
73  		super(b, offset);
74  	}
75  
76  	@Override
77  	@SuppressWarnings("unchecked")
78  	public List<? extends CombinedHunkHeader> getHunks() {
79  		return (List<CombinedHunkHeader>) super.getHunks();
80  	}
81  
82  	/** @return number of ancestor revisions mentioned in this diff. */
83  	@Override
84  	public int getParentCount() {
85  		return oldIds.length;
86  	}
87  
88  	/** @return get the file mode of the first parent. */
89  	@Override
90  	public FileMode getOldMode() {
91  		return getOldMode(0);
92  	}
93  
94  	/**
95  	 * Get the file mode of the nth ancestor
96  	 *
97  	 * @param nthParent
98  	 *            the ancestor to get the mode of
99  	 * @return the mode of the requested ancestor.
100 	 */
101 	public FileMode getOldMode(final int nthParent) {
102 		return oldModes[nthParent];
103 	}
104 
105 	/** @return get the object id of the first parent. */
106 	@Override
107 	public AbbreviatedObjectId getOldId() {
108 		return getOldId(0);
109 	}
110 
111 	/**
112 	 * Get the ObjectId of the nth ancestor
113 	 *
114 	 * @param nthParent
115 	 *            the ancestor to get the object id of
116 	 * @return the id of the requested ancestor.
117 	 */
118 	public AbbreviatedObjectId getOldId(final int nthParent) {
119 		return oldIds[nthParent];
120 	}
121 
122 	@Override
123 	public String getScriptText(final Charset ocs, final Charset ncs) {
124 		final Charset[] cs = new Charset[getParentCount() + 1];
125 		Arrays.fill(cs, ocs);
126 		cs[getParentCount()] = ncs;
127 		return getScriptText(cs);
128 	}
129 
130 	/**
131 	 * Convert the patch script for this file into a string.
132 	 *
133 	 * @param charsetGuess
134 	 *            optional array to suggest the character set to use when
135 	 *            decoding each file's line. If supplied the array must have a
136 	 *            length of <code>{@link #getParentCount()} + 1</code>
137 	 *            representing the old revision character sets and the new
138 	 *            revision character set.
139 	 * @return the patch script, as a Unicode string.
140 	 */
141 	@Override
142 	public String getScriptText(final Charset[] charsetGuess) {
143 		return super.getScriptText(charsetGuess);
144 	}
145 
146 	@Override
147 	int parseGitHeaders(int ptr, final int end) {
148 		while (ptr < end) {
149 			final int eol = nextLF(buf, ptr);
150 			if (isHunkHdr(buf, ptr, end) >= 1) {
151 				// First hunk header; break out and parse them later.
152 				break;
153 
154 			} else if (match(buf, ptr, OLD_NAME) >= 0) {
155 				parseOldName(ptr, eol);
156 
157 			} else if (match(buf, ptr, NEW_NAME) >= 0) {
158 				parseNewName(ptr, eol);
159 
160 			} else if (match(buf, ptr, INDEX) >= 0) {
161 				parseIndexLine(ptr + INDEX.length, eol);
162 
163 			} else if (match(buf, ptr, MODE) >= 0) {
164 				parseModeLine(ptr + MODE.length, eol);
165 
166 			} else if (match(buf, ptr, NEW_FILE_MODE) >= 0) {
167 				parseNewFileMode(ptr, eol);
168 
169 			} else if (match(buf, ptr, DELETED_FILE_MODE) >= 0) {
170 				parseDeletedFileMode(ptr + DELETED_FILE_MODE.length, eol);
171 
172 			} else {
173 				// Probably an empty patch (stat dirty).
174 				break;
175 			}
176 
177 			ptr = eol;
178 		}
179 		return ptr;
180 	}
181 
182 	@Override
183 	protected void parseIndexLine(int ptr, final int eol) {
184 		// "index $asha1,$bsha1..$csha1"
185 		//
186 		final List<AbbreviatedObjectId> ids = new ArrayList<>();
187 		while (ptr < eol) {
188 			final int comma = nextLF(buf, ptr, ',');
189 			if (eol <= comma)
190 				break;
191 			ids.add(AbbreviatedObjectId.fromString(buf, ptr, comma - 1));
192 			ptr = comma;
193 		}
194 
195 		oldIds = new AbbreviatedObjectId[ids.size() + 1];
196 		ids.toArray(oldIds);
197 		final int dot2 = nextLF(buf, ptr, '.');
198 		oldIds[ids.size()] = AbbreviatedObjectId.fromString(buf, ptr, dot2 - 1);
199 		newId = AbbreviatedObjectId.fromString(buf, dot2 + 1, eol - 1);
200 		oldModes = new FileMode[oldIds.length];
201 	}
202 
203 	@Override
204 	protected void parseNewFileMode(final int ptr, final int eol) {
205 		for (int i = 0; i < oldModes.length; i++)
206 			oldModes[i] = FileMode.MISSING;
207 		super.parseNewFileMode(ptr, eol);
208 	}
209 
210 	@Override
211 	HunkHeader newHunkHeader(final int offset) {
212 		return new CombinedHunkHeader(this, offset);
213 	}
214 
215 	private void parseModeLine(int ptr, final int eol) {
216 		// "mode $amode,$bmode..$cmode"
217 		//
218 		int n = 0;
219 		while (ptr < eol) {
220 			final int comma = nextLF(buf, ptr, ',');
221 			if (eol <= comma)
222 				break;
223 			oldModes[n++] = parseFileMode(ptr, comma);
224 			ptr = comma;
225 		}
226 		final int dot2 = nextLF(buf, ptr, '.');
227 		oldModes[n] = parseFileMode(ptr, dot2);
228 		newMode = parseFileMode(dot2 + 1, eol);
229 	}
230 
231 	private void parseDeletedFileMode(int ptr, final int eol) {
232 		// "deleted file mode $amode,$bmode"
233 		//
234 		changeType = ChangeType.DELETE;
235 		int n = 0;
236 		while (ptr < eol) {
237 			final int comma = nextLF(buf, ptr, ',');
238 			if (eol <= comma)
239 				break;
240 			oldModes[n++] = parseFileMode(ptr, comma);
241 			ptr = comma;
242 		}
243 		oldModes[n] = parseFileMode(ptr, eol);
244 		newMode = FileMode.MISSING;
245 	}
246 }