View Javadoc
1   /*
2    * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> 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.api;
12  
13  import static java.nio.charset.StandardCharsets.UTF_8;
14  import static org.junit.Assert.assertEquals;
15  import static org.junit.Assert.assertFalse;
16  import static org.junit.Assert.assertTrue;
17  import static org.junit.Assert.fail;
18  import static org.junit.Assume.assumeFalse;
19  
20  import java.io.File;
21  import java.io.PrintWriter;
22  
23  import org.eclipse.jgit.api.errors.GitAPIException;
24  import org.eclipse.jgit.api.errors.NoMessageException;
25  import org.eclipse.jgit.junit.RepositoryTestCase;
26  import org.eclipse.jgit.lib.Constants;
27  import org.eclipse.jgit.lib.ObjectId;
28  import org.eclipse.jgit.lib.PersonIdent;
29  import org.eclipse.jgit.lib.RefUpdate;
30  import org.eclipse.jgit.lib.ReflogReader;
31  import org.eclipse.jgit.revwalk.RevCommit;
32  import org.eclipse.jgit.treewalk.TreeWalk;
33  import org.eclipse.jgit.util.FS;
34  import org.eclipse.jgit.util.FileUtils;
35  import org.eclipse.jgit.util.RawParseUtils;
36  import org.junit.Test;
37  
38  /**
39   * Testing the git commit and log commands
40   */
41  public class CommitAndLogCommandTest extends RepositoryTestCase {
42  	@Test
43  	public void testSomeCommits() throws Exception {
44  		// do 4 commits
45  		try (Git git = new Git(db)) {
46  			git.commit().setMessage("initial commit").call();
47  			git.commit().setMessage("second commit").setCommitter(committer)
48  					.call();
49  			git.commit().setMessage("third commit").setAuthor(author).call();
50  			git.commit().setMessage("fourth commit").setAuthor(author)
51  					.setCommitter(committer).call();
52  			Iterable<RevCommit> commits = git.log().call();
53  
54  			// check that all commits came in correctly
55  			PersonIdent defaultCommitter = new PersonIdent(db);
56  			PersonIdent expectedAuthors[] = new PersonIdent[] {
57  					defaultCommitter, committer, author, author };
58  			PersonIdent expectedCommitters[] = new PersonIdent[] {
59  					defaultCommitter, committer, defaultCommitter, committer };
60  			String expectedMessages[] = new String[] { "initial commit",
61  					"second commit", "third commit", "fourth commit" };
62  			int l = expectedAuthors.length - 1;
63  			for (RevCommit c : commits) {
64  				assertEquals(expectedAuthors[l].getName(),
65  						c.getAuthorIdent().getName());
66  				assertEquals(expectedCommitters[l].getName(),
67  						c.getCommitterIdent().getName());
68  				assertEquals(c.getFullMessage(), expectedMessages[l]);
69  				l--;
70  			}
71  			assertEquals(l, -1);
72  			ReflogReader reader = db.getReflogReader(Constants.HEAD);
73  			assertTrue(
74  					reader.getLastEntry().getComment().startsWith("commit:"));
75  			reader = db.getReflogReader(db.getBranch());
76  			assertTrue(
77  					reader.getLastEntry().getComment().startsWith("commit:"));
78  		}
79  	}
80  
81  	// try to do a commit without specifying a message. Should fail!
82  	@Test
83  	public void testWrongParams() throws GitAPIException {
84  		try (Git git = new Git(db)) {
85  			git.commit().setAuthor(author).call();
86  			fail("Didn't get the expected exception");
87  		} catch (NoMessageException e) {
88  			// expected
89  		}
90  	}
91  
92  	// try to work with Commands after command has been invoked. Should throw
93  	// exceptions
94  	@Test
95  	public void testMultipleInvocations() throws GitAPIException {
96  		try (Git git = new Git(db)) {
97  			CommitCommand commitCmd = git.commit();
98  			commitCmd.setMessage("initial commit").call();
99  			try {
100 				// check that setters can't be called after invocation
101 				commitCmd.setAuthor(author);
102 				fail("didn't catch the expected exception");
103 			} catch (IllegalStateException e) {
104 				// expected
105 			}
106 			LogCommand logCmd = git.log();
107 			logCmd.call();
108 			try {
109 				// check that call can't be called twice
110 				logCmd.call();
111 				fail("didn't catch the expected exception");
112 			} catch (IllegalStateException e) {
113 				// expected
114 			}
115 		}
116 	}
117 
118 	@Test
119 	public void testMergeEmptyBranches() throws Exception {
120 		try (Git git = new Git(db)) {
121 			git.commit().setMessage("initial commit").call();
122 			RefUpdate r = db.updateRef("refs/heads/side");
123 			r.setNewObjectId(db.resolve(Constants.HEAD));
124 			assertEquals(r.forceUpdate(), RefUpdate.Result.NEW);
125 			RevCommit second = git.commit().setMessage("second commit")
126 					.setCommitter(committer).call();
127 			db.updateRef(Constants.HEAD).link("refs/heads/side");
128 			RevCommit firstSide = git.commit().setMessage("first side commit")
129 					.setAuthor(author).call();
130 
131 			write(new File(db.getDirectory(), Constants.MERGE_HEAD),
132 					ObjectId.toString(db.resolve("refs/heads/master")));
133 			write(new File(db.getDirectory(), Constants.MERGE_MSG), "merging");
134 
135 			RevCommit commit = git.commit().call();
136 			RevCommit[] parents = commit.getParents();
137 			assertEquals(parents[0], firstSide);
138 			assertEquals(parents[1], second);
139 			assertEquals(2, parents.length);
140 		}
141 	}
142 
143 	@Test
144 	public void testAddUnstagedChanges() throws Exception {
145 		File file = new File(db.getWorkTree(), "a.txt");
146 		FileUtils.createNewFile(file);
147 		try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
148 			writer.print("content");
149 		}
150 
151 		try (Git git = new Git(db)) {
152 			git.add().addFilepattern("a.txt").call();
153 			RevCommit commit = git.commit().setMessage("initial commit").call();
154 			TreeWalk tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
155 			assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
156 					tw.getObjectId(0).getName());
157 
158 			try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
159 				writer.print("content2");
160 			}
161 			commit = git.commit().setMessage("second commit").call();
162 			tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
163 			assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
164 					tw.getObjectId(0).getName());
165 
166 			commit = git.commit().setAll(true).setMessage("third commit")
167 					.setAll(true).call();
168 			tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
169 			assertEquals("db00fd65b218578127ea51f3dffac701f12f486a",
170 					tw.getObjectId(0).getName());
171 		}
172 	}
173 
174 	@Test
175 	public void testModeChange() throws Exception {
176 		assumeFalse(System.getProperty("os.name").startsWith("Windows"));// SKIP
177 		try (Git git = new Git(db)) {
178 			// create file
179 			File file = new File(db.getWorkTree(), "a.txt");
180 			FileUtils.createNewFile(file);
181 			try (PrintWriter writer = new PrintWriter(file, UTF_8.name())) {
182 				writer.print("content1");
183 			}
184 
185 			// First commit - a.txt file
186 			git.add().addFilepattern("a.txt").call();
187 			git.commit().setMessage("commit1").setCommitter(committer).call();
188 
189 			// pure mode change should be committable
190 			FS fs = db.getFS();
191 			fs.setExecute(file, true);
192 			git.add().addFilepattern("a.txt").call();
193 			git.commit().setMessage("mode change").setCommitter(committer)
194 					.call();
195 
196 			// pure mode change should be committable with -o option
197 			fs.setExecute(file, false);
198 			git.add().addFilepattern("a.txt").call();
199 			git.commit().setMessage("mode change").setCommitter(committer)
200 					.setOnly("a.txt").call();
201 		}
202 	}
203 
204 	@Test
205 	public void testCommitRange() throws Exception {
206 		// do 4 commits and set the range to the second and fourth one
207 		try (Git git = new Git(db)) {
208 			git.commit().setMessage("first commit").call();
209 			RevCommit second = git.commit().setMessage("second commit")
210 					.setCommitter(committer).call();
211 			git.commit().setMessage("third commit").setAuthor(author).call();
212 			RevCommit last = git.commit().setMessage("fourth commit")
213 					.setAuthor(author).setCommitter(committer).call();
214 			Iterable<RevCommit> commits = git.log()
215 					.addRange(second.getId(), last.getId()).call();
216 
217 			// check that we have the third and fourth commit
218 			PersonIdent defaultCommitter = new PersonIdent(db);
219 			PersonIdent expectedAuthors[] = new PersonIdent[] { author,
220 					author };
221 			PersonIdent expectedCommitters[] = new PersonIdent[] {
222 					defaultCommitter, committer };
223 			String expectedMessages[] = new String[] { "third commit",
224 					"fourth commit" };
225 			int l = expectedAuthors.length - 1;
226 			for (RevCommit c : commits) {
227 				assertEquals(expectedAuthors[l].getName(),
228 						c.getAuthorIdent().getName());
229 				assertEquals(expectedCommitters[l].getName(),
230 						c.getCommitterIdent().getName());
231 				assertEquals(c.getFullMessage(), expectedMessages[l]);
232 				l--;
233 			}
234 			assertEquals(l, -1);
235 		}
236 	}
237 
238 	@Test
239 	public void testCommitAmend() throws Exception {
240 		try (Git git = new Git(db)) {
241 			git.commit().setMessage("first comit").call(); // typo
242 			git.commit().setAmend(true).setMessage("first commit").call();
243 
244 			Iterable<RevCommit> commits = git.log().call();
245 			int c = 0;
246 			for (RevCommit commit : commits) {
247 				assertEquals("first commit", commit.getFullMessage());
248 				c++;
249 			}
250 			assertEquals(1, c);
251 			ReflogReader reader = db.getReflogReader(Constants.HEAD);
252 			assertTrue(reader.getLastEntry().getComment()
253 					.startsWith("commit (amend):"));
254 			reader = db.getReflogReader(db.getBranch());
255 			assertTrue(reader.getLastEntry().getComment()
256 					.startsWith("commit (amend):"));
257 		}
258 	}
259 
260 	@Test
261 	public void testInsertChangeId() throws Exception {
262 		try (Git git = new Git(db)) {
263 			String messageHeader = "Some header line\n\nSome detail explanation\n";
264 			String changeIdTemplate = "\nChange-Id: I"
265 					+ ObjectId.zeroId().getName() + "\n";
266 			String messageFooter = "Some foooter lines\nAnother footer line\n";
267 			RevCommit commit = git.commit()
268 					.setMessage(messageHeader + messageFooter)
269 					.setInsertChangeId(true).call();
270 			// we should find a real change id (at the end of the file)
271 			byte[] chars = commit.getFullMessage().getBytes(UTF_8);
272 			int lastLineBegin = RawParseUtils.prevLF(chars, chars.length - 2);
273 			String lastLine = RawParseUtils.decode(chars, lastLineBegin + 1,
274 					chars.length);
275 			assertTrue(lastLine.contains("Change-Id:"));
276 			assertFalse(lastLine
277 					.contains("Change-Id: I" + ObjectId.zeroId().getName()));
278 
279 			commit = git.commit()
280 					.setMessage(
281 							messageHeader + changeIdTemplate + messageFooter)
282 					.setInsertChangeId(true).call();
283 			// we should find a real change id (in the line as dictated by the
284 			// template)
285 			chars = commit.getFullMessage().getBytes(UTF_8);
286 			int lineStart = 0;
287 			int lineEnd = 0;
288 			for (int i = 0; i < 4; i++) {
289 				lineStart = RawParseUtils.nextLF(chars, lineStart);
290 			}
291 			lineEnd = RawParseUtils.nextLF(chars, lineStart);
292 
293 			String line = RawParseUtils.decode(chars, lineStart, lineEnd);
294 
295 			assertTrue(line.contains("Change-Id:"));
296 			assertFalse(line
297 					.contains("Change-Id: I" + ObjectId.zeroId().getName()));
298 
299 			commit = git.commit()
300 					.setMessage(
301 							messageHeader + changeIdTemplate + messageFooter)
302 					.setInsertChangeId(false).call();
303 			// we should find the untouched template
304 			chars = commit.getFullMessage().getBytes(UTF_8);
305 			lineStart = 0;
306 			lineEnd = 0;
307 			for (int i = 0; i < 4; i++) {
308 				lineStart = RawParseUtils.nextLF(chars, lineStart);
309 			}
310 			lineEnd = RawParseUtils.nextLF(chars, lineStart);
311 
312 			line = RawParseUtils.decode(chars, lineStart, lineEnd);
313 
314 			assertTrue(commit.getFullMessage()
315 					.contains("Change-Id: I" + ObjectId.zeroId().getName()));
316 		}
317 	}
318 }