xref: /JGit/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java (revision 0bd2f4bf77c856213e09d656a948e71f71cfd038)
1767be14fSRobin Rosenberg /*
2767be14fSRobin Rosenberg  * Copyright (C) 2009, Google Inc.
3767be14fSRobin Rosenberg  * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
4767be14fSRobin Rosenberg  * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org>
55c5f7c6bSMatthias Sohn  * Copyright (C) 2009, Yann Simon <yann.simon.fr@gmail.com> and others
6767be14fSRobin Rosenberg  *
75c5f7c6bSMatthias Sohn  * This program and the accompanying materials are made available under the
85c5f7c6bSMatthias Sohn  * terms of the Eclipse Distribution License v. 1.0 which is available at
95c5f7c6bSMatthias Sohn  * https://www.eclipse.org/org/documents/edl-v10.php.
10767be14fSRobin Rosenberg  *
115c5f7c6bSMatthias Sohn  * SPDX-License-Identifier: BSD-3-Clause
12767be14fSRobin Rosenberg  */
13767be14fSRobin Rosenberg 
14767be14fSRobin Rosenberg package org.eclipse.jgit.junit;
15767be14fSRobin Rosenberg 
1630c6c754SDavid Pursehouse import static java.nio.charset.StandardCharsets.UTF_8;
17767be14fSRobin Rosenberg import static org.junit.Assert.assertEquals;
18767be14fSRobin Rosenberg 
19767be14fSRobin Rosenberg import java.io.File;
20767be14fSRobin Rosenberg import java.io.FileInputStream;
21767be14fSRobin Rosenberg import java.io.FileNotFoundException;
22767be14fSRobin Rosenberg import java.io.FileOutputStream;
23767be14fSRobin Rosenberg import java.io.IOException;
24767be14fSRobin Rosenberg import java.io.InputStreamReader;
25767be14fSRobin Rosenberg import java.io.Reader;
26a406ebf4SAndrey Loskutov import java.nio.file.Path;
2795e8264cSMatthias Sohn import java.time.Instant;
28*0bd2f4bfSAdithya Chakilam import java.util.List;
29767be14fSRobin Rosenberg import java.util.Map;
3095e8264cSMatthias Sohn import java.util.concurrent.TimeUnit;
31767be14fSRobin Rosenberg 
32767be14fSRobin Rosenberg import org.eclipse.jgit.api.Git;
33767be14fSRobin Rosenberg import org.eclipse.jgit.api.errors.GitAPIException;
34767be14fSRobin Rosenberg import org.eclipse.jgit.dircache.DirCacheBuilder;
35767be14fSRobin Rosenberg import org.eclipse.jgit.dircache.DirCacheCheckout;
36767be14fSRobin Rosenberg import org.eclipse.jgit.dircache.DirCacheEntry;
37f32b8612SShawn Pearce import org.eclipse.jgit.internal.storage.file.FileRepository;
38e60ea732SDemetr Starshov import org.eclipse.jgit.lib.AnyObjectId;
39767be14fSRobin Rosenberg import org.eclipse.jgit.lib.Constants;
40767be14fSRobin Rosenberg import org.eclipse.jgit.lib.FileMode;
41767be14fSRobin Rosenberg import org.eclipse.jgit.lib.ObjectId;
42767be14fSRobin Rosenberg import org.eclipse.jgit.lib.ObjectInserter;
43*0bd2f4bfSAdithya Chakilam import org.eclipse.jgit.lib.Ref;
44767be14fSRobin Rosenberg import org.eclipse.jgit.lib.RefUpdate;
45767be14fSRobin Rosenberg import org.eclipse.jgit.lib.Repository;
46767be14fSRobin Rosenberg import org.eclipse.jgit.revwalk.RevCommit;
47767be14fSRobin Rosenberg import org.eclipse.jgit.revwalk.RevWalk;
48767be14fSRobin Rosenberg import org.eclipse.jgit.treewalk.FileTreeIterator;
49dd318160SRobin Rosenberg import org.eclipse.jgit.util.FS;
50767be14fSRobin Rosenberg import org.eclipse.jgit.util.FileUtils;
515dcc4659SNail Samatov import org.junit.After;
52767be14fSRobin Rosenberg import org.junit.Before;
53767be14fSRobin Rosenberg 
54767be14fSRobin Rosenberg /**
55767be14fSRobin Rosenberg  * Base class for most JGit unit tests.
56767be14fSRobin Rosenberg  *
57767be14fSRobin Rosenberg  * Sets up a predefined test repository and has support for creating additional
58767be14fSRobin Rosenberg  * repositories and destroying them when the tests are finished.
59767be14fSRobin Rosenberg  */
60767be14fSRobin Rosenberg public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
61a90b75b4SMatthias Sohn 	/**
62a90b75b4SMatthias Sohn 	 * Copy a file
63a90b75b4SMatthias Sohn 	 *
64a90b75b4SMatthias Sohn 	 * @param src
65a90b75b4SMatthias Sohn 	 * @param dst
66a90b75b4SMatthias Sohn 	 * @throws IOException
67a90b75b4SMatthias Sohn 	 */
copyFile(File src, File dst)686d370d83SHan-Wen Nienhuys 	protected static void copyFile(File src, File dst)
69767be14fSRobin Rosenberg 			throws IOException {
705c70be00SDavid Pursehouse 		try (FileInputStream fis = new FileInputStream(src);
715c70be00SDavid Pursehouse 				FileOutputStream fos = new FileOutputStream(dst)) {
72767be14fSRobin Rosenberg 			final byte[] buf = new byte[4096];
73767be14fSRobin Rosenberg 			int r;
74767be14fSRobin Rosenberg 			while ((r = fis.read(buf)) > 0) {
75767be14fSRobin Rosenberg 				fos.write(buf, 0, r);
76767be14fSRobin Rosenberg 			}
77767be14fSRobin Rosenberg 		}
78767be14fSRobin Rosenberg 	}
79767be14fSRobin Rosenberg 
80a90b75b4SMatthias Sohn 	/**
81a90b75b4SMatthias Sohn 	 * Write a trash file
82a90b75b4SMatthias Sohn 	 *
83a90b75b4SMatthias Sohn 	 * @param name
84a90b75b4SMatthias Sohn 	 * @param data
85a90b75b4SMatthias Sohn 	 * @return the trash file
86a90b75b4SMatthias Sohn 	 * @throws IOException
87a90b75b4SMatthias Sohn 	 */
writeTrashFile(String name, String data)886d370d83SHan-Wen Nienhuys 	protected File writeTrashFile(String name, String data)
89767be14fSRobin Rosenberg 			throws IOException {
90767be14fSRobin Rosenberg 		return JGitTestUtil.writeTrashFile(db, name, data);
91767be14fSRobin Rosenberg 	}
92767be14fSRobin Rosenberg 
932cdc130dSMatthias Sohn 	/**
942cdc130dSMatthias Sohn 	 * Create a symbolic link
952cdc130dSMatthias Sohn 	 *
962cdc130dSMatthias Sohn 	 * @param link
972cdc130dSMatthias Sohn 	 *            the path of the symbolic link to create
982cdc130dSMatthias Sohn 	 * @param target
992cdc130dSMatthias Sohn 	 *            the target of the symbolic link
1002cdc130dSMatthias Sohn 	 * @return the path to the symbolic link
1012cdc130dSMatthias Sohn 	 * @throws Exception
1022cdc130dSMatthias Sohn 	 * @since 4.2
1032cdc130dSMatthias Sohn 	 */
writeLink(String link, String target)1046d370d83SHan-Wen Nienhuys 	protected Path writeLink(String link, String target)
105a406ebf4SAndrey Loskutov 			throws Exception {
106a406ebf4SAndrey Loskutov 		return JGitTestUtil.writeLink(db, link, target);
107a406ebf4SAndrey Loskutov 	}
108a406ebf4SAndrey Loskutov 
109a90b75b4SMatthias Sohn 	/**
110a90b75b4SMatthias Sohn 	 * Write a trash file
111a90b75b4SMatthias Sohn 	 *
112a90b75b4SMatthias Sohn 	 * @param subdir
113a90b75b4SMatthias Sohn 	 * @param name
114a90b75b4SMatthias Sohn 	 * @param data
115a90b75b4SMatthias Sohn 	 * @return the trash file
116a90b75b4SMatthias Sohn 	 * @throws IOException
117a90b75b4SMatthias Sohn 	 */
writeTrashFile(final String subdir, final String name, final String data)118767be14fSRobin Rosenberg 	protected File writeTrashFile(final String subdir, final String name,
119767be14fSRobin Rosenberg 			final String data)
120767be14fSRobin Rosenberg 			throws IOException {
121767be14fSRobin Rosenberg 		return JGitTestUtil.writeTrashFile(db, subdir, name, data);
122767be14fSRobin Rosenberg 	}
123767be14fSRobin Rosenberg 
124a90b75b4SMatthias Sohn 	/**
125a90b75b4SMatthias Sohn 	 * Read content of a file
126a90b75b4SMatthias Sohn 	 *
127a90b75b4SMatthias Sohn 	 * @param name
128a90b75b4SMatthias Sohn 	 * @return the file's content
129a90b75b4SMatthias Sohn 	 * @throws IOException
130a90b75b4SMatthias Sohn 	 */
read(String name)1316d370d83SHan-Wen Nienhuys 	protected String read(String name) throws IOException {
132767be14fSRobin Rosenberg 		return JGitTestUtil.read(db, name);
133767be14fSRobin Rosenberg 	}
134767be14fSRobin Rosenberg 
135a90b75b4SMatthias Sohn 	/**
136a90b75b4SMatthias Sohn 	 * Check if file exists
137a90b75b4SMatthias Sohn 	 *
138a90b75b4SMatthias Sohn 	 * @param name
139a90b75b4SMatthias Sohn 	 *            file name
140a90b75b4SMatthias Sohn 	 * @return if the file exists
141a90b75b4SMatthias Sohn 	 */
check(String name)1426d370d83SHan-Wen Nienhuys 	protected boolean check(String name) {
1430c4553d2SChristian Halstrick 		return JGitTestUtil.check(db, name);
1440c4553d2SChristian Halstrick 	}
1450c4553d2SChristian Halstrick 
146a90b75b4SMatthias Sohn 	/**
147a90b75b4SMatthias Sohn 	 * Delete a trash file
148a90b75b4SMatthias Sohn 	 *
149a90b75b4SMatthias Sohn 	 * @param name
150a90b75b4SMatthias Sohn 	 *            file name
151a90b75b4SMatthias Sohn 	 * @throws IOException
152a90b75b4SMatthias Sohn 	 */
deleteTrashFile(String name)1536d370d83SHan-Wen Nienhuys 	protected void deleteTrashFile(String name) throws IOException {
154767be14fSRobin Rosenberg 		JGitTestUtil.deleteTrashFile(db, name);
155767be14fSRobin Rosenberg 	}
156767be14fSRobin Rosenberg 
157a90b75b4SMatthias Sohn 	/**
158a90b75b4SMatthias Sohn 	 * Check content of a file.
159a90b75b4SMatthias Sohn 	 *
160a90b75b4SMatthias Sohn 	 * @param f
161a90b75b4SMatthias Sohn 	 * @param checkData
162a90b75b4SMatthias Sohn 	 *            expected content
163a90b75b4SMatthias Sohn 	 * @throws IOException
164a90b75b4SMatthias Sohn 	 */
checkFile(File f, String checkData)1656d370d83SHan-Wen Nienhuys 	protected static void checkFile(File f, String checkData)
166767be14fSRobin Rosenberg 			throws IOException {
167fc7d407dSThomas Wolf 		try (Reader r = new InputStreamReader(new FileInputStream(f),
16830c6c754SDavid Pursehouse 				UTF_8)) {
169fc7d407dSThomas Wolf 			if (checkData.length() > 0) {
170770d36c8SXinTong Wang 				char[] data = new char[checkData.length()];
171fc7d407dSThomas Wolf 				assertEquals(data.length, r.read(data));
172767be14fSRobin Rosenberg 				assertEquals(checkData, new String(data));
173fc7d407dSThomas Wolf 			}
174fc7d407dSThomas Wolf 			assertEquals(-1, r.read());
175767be14fSRobin Rosenberg 		}
176767be14fSRobin Rosenberg 	}
177767be14fSRobin Rosenberg 
178767be14fSRobin Rosenberg 	/** Test repository, initialized for this test case. */
179767be14fSRobin Rosenberg 	protected FileRepository db;
180767be14fSRobin Rosenberg 
181767be14fSRobin Rosenberg 	/** Working directory of {@link #db}. */
182767be14fSRobin Rosenberg 	protected File trash;
183767be14fSRobin Rosenberg 
184a90b75b4SMatthias Sohn 	/** {@inheritDoc} */
185767be14fSRobin Rosenberg 	@Override
186767be14fSRobin Rosenberg 	@Before
setUp()187767be14fSRobin Rosenberg 	public void setUp() throws Exception {
188767be14fSRobin Rosenberg 		super.setUp();
189767be14fSRobin Rosenberg 		db = createWorkRepository();
190767be14fSRobin Rosenberg 		trash = db.getWorkTree();
191767be14fSRobin Rosenberg 	}
192767be14fSRobin Rosenberg 
1935dcc4659SNail Samatov 	@Override
1945dcc4659SNail Samatov 	@After
tearDown()1955dcc4659SNail Samatov 	public void tearDown() throws Exception {
1965dcc4659SNail Samatov 		db.close();
1975dcc4659SNail Samatov 		super.tearDown();
1985dcc4659SNail Samatov 	}
1995dcc4659SNail Samatov 
200767be14fSRobin Rosenberg 	/**
201767be14fSRobin Rosenberg 	 * Represent the state of the index in one String. This representation is
202767be14fSRobin Rosenberg 	 * useful when writing tests which do assertions on the state of the index.
203767be14fSRobin Rosenberg 	 * By default information about path, mode, stage (if different from 0) is
204767be14fSRobin Rosenberg 	 * included. A bitmask controls which additional info about
205767be14fSRobin Rosenberg 	 * modificationTimes, smudge state and length is included.
206767be14fSRobin Rosenberg 	 * <p>
207767be14fSRobin Rosenberg 	 * The format of the returned string is described with this BNF:
208767be14fSRobin Rosenberg 	 *
209767be14fSRobin Rosenberg 	 * <pre>
210767be14fSRobin Rosenberg 	 * result = ( "[" path mode stage? time? smudge? length? sha1? content? "]" )* .
211767be14fSRobin Rosenberg 	 * mode = ", mode:" number .
212767be14fSRobin Rosenberg 	 * stage = ", stage:" number .
213767be14fSRobin Rosenberg 	 * time = ", time:t" timestamp-index .
214767be14fSRobin Rosenberg 	 * smudge = "" | ", smudged" .
215767be14fSRobin Rosenberg 	 * length = ", length:" number .
216767be14fSRobin Rosenberg 	 * sha1 = ", sha1:" hex-sha1 .
217767be14fSRobin Rosenberg 	 * content = ", content:" blob-data .
218767be14fSRobin Rosenberg 	 * </pre>
219767be14fSRobin Rosenberg 	 *
220767be14fSRobin Rosenberg 	 * 'stage' is only presented when the stage is different from 0. All
221767be14fSRobin Rosenberg 	 * reported time stamps are mapped to strings like "t0", "t1", ... "tn". The
222767be14fSRobin Rosenberg 	 * smallest reported time-stamp will be called "t0". This allows to write
223767be14fSRobin Rosenberg 	 * assertions against the string although the concrete value of the time
224767be14fSRobin Rosenberg 	 * stamps is unknown.
225767be14fSRobin Rosenberg 	 *
226767be14fSRobin Rosenberg 	 * @param includedOptions
227767be14fSRobin Rosenberg 	 *            a bitmask constructed out of the constants {@link #MOD_TIME},
228767be14fSRobin Rosenberg 	 *            {@link #SMUDGE}, {@link #LENGTH}, {@link #CONTENT_ID} and
229767be14fSRobin Rosenberg 	 *            {@link #CONTENT} controlling which info is present in the
230767be14fSRobin Rosenberg 	 *            resulting string.
231767be14fSRobin Rosenberg 	 * @return a string encoding the index state
232767be14fSRobin Rosenberg 	 * @throws IllegalStateException
233767be14fSRobin Rosenberg 	 * @throws IOException
234767be14fSRobin Rosenberg 	 */
indexState(int includedOptions)235767be14fSRobin Rosenberg 	public String indexState(int includedOptions)
236767be14fSRobin Rosenberg 			throws IllegalStateException, IOException {
237767be14fSRobin Rosenberg 		return indexState(db, includedOptions);
238767be14fSRobin Rosenberg 	}
239767be14fSRobin Rosenberg 
240767be14fSRobin Rosenberg 	/**
241767be14fSRobin Rosenberg 	 * Resets the index to represent exactly some filesystem content. E.g. the
242767be14fSRobin Rosenberg 	 * following call will replace the index with the working tree content:
243767be14fSRobin Rosenberg 	 * <p>
244767be14fSRobin Rosenberg 	 * <code>resetIndex(new FileSystemIterator(db))</code>
245767be14fSRobin Rosenberg 	 * <p>
246767be14fSRobin Rosenberg 	 * This method can be used by testcases which first prepare a new commit
247767be14fSRobin Rosenberg 	 * somewhere in the filesystem (e.g. in the working-tree) and then want to
248767be14fSRobin Rosenberg 	 * have an index which matches their prepared content.
249767be14fSRobin Rosenberg 	 *
250767be14fSRobin Rosenberg 	 * @param treeItr
251a90b75b4SMatthias Sohn 	 *            a {@link org.eclipse.jgit.treewalk.FileTreeIterator} which
252a90b75b4SMatthias Sohn 	 *            determines which files should go into the new index
253767be14fSRobin Rosenberg 	 * @throws FileNotFoundException
254767be14fSRobin Rosenberg 	 * @throws IOException
255767be14fSRobin Rosenberg 	 */
resetIndex(FileTreeIterator treeItr)256767be14fSRobin Rosenberg 	protected void resetIndex(FileTreeIterator treeItr)
257767be14fSRobin Rosenberg 			throws FileNotFoundException, IOException {
258686124beSMatthias Sohn 		try (ObjectInserter inserter = db.newObjectInserter()) {
259767be14fSRobin Rosenberg 			DirCacheBuilder builder = db.lockDirCache().builder();
260767be14fSRobin Rosenberg 			DirCacheEntry dce;
261767be14fSRobin Rosenberg 
262767be14fSRobin Rosenberg 			while (!treeItr.eof()) {
263767be14fSRobin Rosenberg 				long len = treeItr.getEntryLength();
264767be14fSRobin Rosenberg 
265767be14fSRobin Rosenberg 				dce = new DirCacheEntry(treeItr.getEntryPathString());
266767be14fSRobin Rosenberg 				dce.setFileMode(treeItr.getEntryFileMode());
26795e8264cSMatthias Sohn 				dce.setLastModified(treeItr.getEntryLastModifiedInstant());
268767be14fSRobin Rosenberg 				dce.setLength((int) len);
2695c70be00SDavid Pursehouse 				try (FileInputStream in = new FileInputStream(
2705c70be00SDavid Pursehouse 						treeItr.getEntryFile())) {
2715c70be00SDavid Pursehouse 					dce.setObjectId(
2725c70be00SDavid Pursehouse 							inserter.insert(Constants.OBJ_BLOB, len, in));
2735c70be00SDavid Pursehouse 				}
274767be14fSRobin Rosenberg 				builder.add(dce);
275767be14fSRobin Rosenberg 				treeItr.next(1);
276767be14fSRobin Rosenberg 			}
277767be14fSRobin Rosenberg 			builder.commit();
278767be14fSRobin Rosenberg 			inserter.flush();
279686124beSMatthias Sohn 		}
280767be14fSRobin Rosenberg 	}
281767be14fSRobin Rosenberg 
282767be14fSRobin Rosenberg 	/**
283767be14fSRobin Rosenberg 	 * Helper method to map arbitrary objects to user-defined names. This can be
284767be14fSRobin Rosenberg 	 * used create short names for objects to produce small and stable debug
285767be14fSRobin Rosenberg 	 * output. It is guaranteed that when you lookup the same object multiple
286767be14fSRobin Rosenberg 	 * times even with different nameTemplates this method will always return
287767be14fSRobin Rosenberg 	 * the same name which was derived from the first nameTemplate.
288767be14fSRobin Rosenberg 	 * nameTemplates can contain "%n" which will be replaced by a running number
289767be14fSRobin Rosenberg 	 * before used as a name.
290767be14fSRobin Rosenberg 	 *
291767be14fSRobin Rosenberg 	 * @param l
292767be14fSRobin Rosenberg 	 *            the object to lookup
293a90b75b4SMatthias Sohn 	 * @param lookupTable
294a90b75b4SMatthias Sohn 	 *            a table storing object-name mappings.
295767be14fSRobin Rosenberg 	 * @param nameTemplate
296767be14fSRobin Rosenberg 	 *            the name for that object. Can contain "%n" which will be
297767be14fSRobin Rosenberg 	 *            replaced by a running number before used as a name. If the
298767be14fSRobin Rosenberg 	 *            lookup table already contains the object this parameter will
299767be14fSRobin Rosenberg 	 *            be ignored
300767be14fSRobin Rosenberg 	 * @return a name of that object. Is not guaranteed to be unique. Use
301767be14fSRobin Rosenberg 	 *         nameTemplates containing "%n" to always have unique names
302767be14fSRobin Rosenberg 	 */
lookup(Object l, String nameTemplate, Map<Object, String> lookupTable)303767be14fSRobin Rosenberg 	public static String lookup(Object l, String nameTemplate,
304767be14fSRobin Rosenberg 			Map<Object, String> lookupTable) {
305767be14fSRobin Rosenberg 		String name = lookupTable.get(l);
306767be14fSRobin Rosenberg 		if (name == null) {
307767be14fSRobin Rosenberg 			name = nameTemplate.replaceAll("%n",
308767be14fSRobin Rosenberg 					Integer.toString(lookupTable.size()));
309767be14fSRobin Rosenberg 			lookupTable.put(l, name);
310767be14fSRobin Rosenberg 		}
311767be14fSRobin Rosenberg 		return name;
312767be14fSRobin Rosenberg 	}
313767be14fSRobin Rosenberg 
314767be14fSRobin Rosenberg 	/**
3155d9f595eSChristian Halstrick 	 * Replaces '\' by '/'
3165d9f595eSChristian Halstrick 	 *
3175d9f595eSChristian Halstrick 	 * @param str
3185d9f595eSChristian Halstrick 	 *            the string in which backslashes should be replaced
3195d9f595eSChristian Halstrick 	 * @return the resulting string with slashes
3205d9f595eSChristian Halstrick 	 * @since 4.2
3215d9f595eSChristian Halstrick 	 */
slashify(String str)3225d9f595eSChristian Halstrick 	public static String slashify(String str) {
3235d9f595eSChristian Halstrick 		str = str.replace('\\', '/');
3245d9f595eSChristian Halstrick 		return str;
3255d9f595eSChristian Halstrick 	}
3265d9f595eSChristian Halstrick 
3275d9f595eSChristian Halstrick 	/**
328767be14fSRobin Rosenberg 	 * Waits until it is guaranteed that a subsequent file modification has a
329767be14fSRobin Rosenberg 	 * younger modification timestamp than the modification timestamp of the
330767be14fSRobin Rosenberg 	 * given file. This is done by touching a temporary file, reading the
331767be14fSRobin Rosenberg 	 * lastmodified attribute and, if needed, sleeping. After sleeping this loop
332b2ee9cfbSChristian Halstrick 	 * starts again until the filesystem timer has advanced enough. The
333b2ee9cfbSChristian Halstrick 	 * temporary file will be created as a sibling of lastFile.
334767be14fSRobin Rosenberg 	 *
335767be14fSRobin Rosenberg 	 * @param lastFile
336767be14fSRobin Rosenberg 	 *            the file on which we want to wait until the filesystem timer
337767be14fSRobin Rosenberg 	 *            has advanced more than the lastmodification timestamp of this
338767be14fSRobin Rosenberg 	 *            file
339767be14fSRobin Rosenberg 	 * @return return the last measured value of the filesystem timer which is
340767be14fSRobin Rosenberg 	 *         greater than then the lastmodification time of lastfile.
341767be14fSRobin Rosenberg 	 * @throws InterruptedException
342767be14fSRobin Rosenberg 	 * @throws IOException
343767be14fSRobin Rosenberg 	 */
fsTick(File lastFile)34495e8264cSMatthias Sohn 	public static Instant fsTick(File lastFile)
34595e8264cSMatthias Sohn 			throws InterruptedException,
346767be14fSRobin Rosenberg 			IOException {
347b2ee9cfbSChristian Halstrick 		File tmp;
348dd318160SRobin Rosenberg 		FS fs = FS.DETECTED;
349b2ee9cfbSChristian Halstrick 		if (lastFile == null) {
350b2ee9cfbSChristian Halstrick 			lastFile = tmp = File
351b2ee9cfbSChristian Halstrick 					.createTempFile("fsTickTmpFile", null);
352b2ee9cfbSChristian Halstrick 		} else {
353b2ee9cfbSChristian Halstrick 			if (!fs.exists(lastFile)) {
354767be14fSRobin Rosenberg 				throw new FileNotFoundException(lastFile.getPath());
355b2ee9cfbSChristian Halstrick 			}
356b2ee9cfbSChristian Halstrick 			tmp = File.createTempFile("fsTickTmpFile", null,
357b2ee9cfbSChristian Halstrick 					lastFile.getParentFile());
358b2ee9cfbSChristian Halstrick 		}
359d45219baSMatthias Sohn 		long res = FS.getFileStoreAttributes(tmp.toPath())
3605911521bSMatthias Sohn 				.getFsTimestampResolution().toNanos();
361b2ee9cfbSChristian Halstrick 		long sleepTime = res / 10;
362767be14fSRobin Rosenberg 		try {
36395e8264cSMatthias Sohn 			Instant startTime = fs.lastModifiedInstant(lastFile);
36495e8264cSMatthias Sohn 			Instant actTime = fs.lastModifiedInstant(tmp);
36595e8264cSMatthias Sohn 			while (actTime.compareTo(startTime) <= 0) {
36695e8264cSMatthias Sohn 				TimeUnit.NANOSECONDS.sleep(sleepTime);
367b513b774SMatthias Sohn 				FileUtils.touch(tmp.toPath());
36895e8264cSMatthias Sohn 				actTime = fs.lastModifiedInstant(tmp);
369767be14fSRobin Rosenberg 			}
370767be14fSRobin Rosenberg 			return actTime;
371767be14fSRobin Rosenberg 		} finally {
372767be14fSRobin Rosenberg 			FileUtils.delete(tmp);
373767be14fSRobin Rosenberg 		}
374767be14fSRobin Rosenberg 	}
375767be14fSRobin Rosenberg 
376a90b75b4SMatthias Sohn 	/**
377a90b75b4SMatthias Sohn 	 * Create a branch
378a90b75b4SMatthias Sohn 	 *
379a90b75b4SMatthias Sohn 	 * @param objectId
380a90b75b4SMatthias Sohn 	 * @param branchName
381a90b75b4SMatthias Sohn 	 * @throws IOException
382a90b75b4SMatthias Sohn 	 */
createBranch(ObjectId objectId, String branchName)383767be14fSRobin Rosenberg 	protected void createBranch(ObjectId objectId, String branchName)
384767be14fSRobin Rosenberg 			throws IOException {
385767be14fSRobin Rosenberg 		RefUpdate updateRef = db.updateRef(branchName);
386767be14fSRobin Rosenberg 		updateRef.setNewObjectId(objectId);
387767be14fSRobin Rosenberg 		updateRef.update();
388767be14fSRobin Rosenberg 	}
389767be14fSRobin Rosenberg 
390a90b75b4SMatthias Sohn 	/**
391*0bd2f4bfSAdithya Chakilam 	 * Get all Refs
392*0bd2f4bfSAdithya Chakilam 	 *
393*0bd2f4bfSAdithya Chakilam 	 * @return list of refs
394*0bd2f4bfSAdithya Chakilam 	 * @throws IOException
395*0bd2f4bfSAdithya Chakilam 	 */
getRefs()396*0bd2f4bfSAdithya Chakilam 	public List<Ref> getRefs() throws IOException {
397*0bd2f4bfSAdithya Chakilam 		return db.getRefDatabase().getRefs();
398*0bd2f4bfSAdithya Chakilam 	}
399*0bd2f4bfSAdithya Chakilam 
400*0bd2f4bfSAdithya Chakilam 	/**
401a90b75b4SMatthias Sohn 	 * Checkout a branch
402a90b75b4SMatthias Sohn 	 *
403a90b75b4SMatthias Sohn 	 * @param branchName
404a90b75b4SMatthias Sohn 	 * @throws IllegalStateException
405a90b75b4SMatthias Sohn 	 * @throws IOException
406a90b75b4SMatthias Sohn 	 */
checkoutBranch(String branchName)407767be14fSRobin Rosenberg 	protected void checkoutBranch(String branchName)
408767be14fSRobin Rosenberg 			throws IllegalStateException, IOException {
409686124beSMatthias Sohn 		try (RevWalk walk = new RevWalk(db)) {
410767be14fSRobin Rosenberg 			RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD));
411767be14fSRobin Rosenberg 			RevCommit branch = walk.parseCommit(db.resolve(branchName));
412686124beSMatthias Sohn 			DirCacheCheckout dco = new DirCacheCheckout(db,
413686124beSMatthias Sohn 					head.getTree().getId(), db.lockDirCache(),
414686124beSMatthias Sohn 					branch.getTree().getId());
415767be14fSRobin Rosenberg 			dco.setFailOnConflict(true);
416767be14fSRobin Rosenberg 			dco.checkout();
417686124beSMatthias Sohn 		}
418767be14fSRobin Rosenberg 		// update the HEAD
419767be14fSRobin Rosenberg 		RefUpdate refUpdate = db.updateRef(Constants.HEAD);
42060e01cffSRobin Rosenberg 		refUpdate.setRefLogMessage("checkout: moving to " + branchName, false);
421767be14fSRobin Rosenberg 		refUpdate.link(branchName);
422767be14fSRobin Rosenberg 	}
423767be14fSRobin Rosenberg 
424767be14fSRobin Rosenberg 	/**
425767be14fSRobin Rosenberg 	 * Writes a number of files in the working tree. The first content specified
426767be14fSRobin Rosenberg 	 * will be written into a file named '0', the second into a file named "1"
427767be14fSRobin Rosenberg 	 * and so on. If <code>null</code> is specified as content then this file is
428767be14fSRobin Rosenberg 	 * skipped.
429767be14fSRobin Rosenberg 	 *
430767be14fSRobin Rosenberg 	 * @param ensureDistinctTimestamps
431767be14fSRobin Rosenberg 	 *            if set to <code>true</code> then between two write operations
432767be14fSRobin Rosenberg 	 *            this method will wait to ensure that the second file will get
433767be14fSRobin Rosenberg 	 *            a different lastmodification timestamp than the first file.
434767be14fSRobin Rosenberg 	 * @param contents
435767be14fSRobin Rosenberg 	 *            the contents which should be written into the files
436767be14fSRobin Rosenberg 	 * @return the File object associated to the last written file.
437767be14fSRobin Rosenberg 	 * @throws IOException
438767be14fSRobin Rosenberg 	 * @throws InterruptedException
439767be14fSRobin Rosenberg 	 */
writeTrashFiles(boolean ensureDistinctTimestamps, String... contents)440767be14fSRobin Rosenberg 	protected File writeTrashFiles(boolean ensureDistinctTimestamps,
441767be14fSRobin Rosenberg 			String... contents)
442767be14fSRobin Rosenberg 			throws IOException, InterruptedException {
443767be14fSRobin Rosenberg 				File f = null;
444767be14fSRobin Rosenberg 				for (int i = 0; i < contents.length; i++)
445767be14fSRobin Rosenberg 					if (contents[i] != null) {
446767be14fSRobin Rosenberg 						if (ensureDistinctTimestamps && (f != null))
447767be14fSRobin Rosenberg 							fsTick(f);
448767be14fSRobin Rosenberg 						f = writeTrashFile(Integer.toString(i), contents[i]);
449767be14fSRobin Rosenberg 					}
450767be14fSRobin Rosenberg 				return f;
451767be14fSRobin Rosenberg 			}
452767be14fSRobin Rosenberg 
453767be14fSRobin Rosenberg 	/**
454767be14fSRobin Rosenberg 	 * Commit a file with the specified contents on the specified branch,
455767be14fSRobin Rosenberg 	 * creating the branch if it didn't exist before.
456767be14fSRobin Rosenberg 	 * <p>
457767be14fSRobin Rosenberg 	 * It switches back to the original branch after the commit if there was
458767be14fSRobin Rosenberg 	 * one.
459767be14fSRobin Rosenberg 	 *
460767be14fSRobin Rosenberg 	 * @param filename
461767be14fSRobin Rosenberg 	 * @param contents
462767be14fSRobin Rosenberg 	 * @param branch
463767be14fSRobin Rosenberg 	 * @return the created commit
464767be14fSRobin Rosenberg 	 */
commitFile(String filename, String contents, String branch)465767be14fSRobin Rosenberg 	protected RevCommit commitFile(String filename, String contents, String branch) {
466b85e9304SDavid Pursehouse 		try (Git git = new Git(db)) {
467e6492875SMatthias Sohn 			Repository repo = git.getRepository();
468e6492875SMatthias Sohn 			String originalBranch = repo.getFullBranch();
469e6492875SMatthias Sohn 			boolean empty = repo.resolve(Constants.HEAD) == null;
470e6492875SMatthias Sohn 			if (!empty) {
4714c236ff4SMatthias Sohn 				if (repo.findRef(branch) == null)
472767be14fSRobin Rosenberg 					git.branchCreate().setName(branch).call();
473767be14fSRobin Rosenberg 				git.checkout().setName(branch).call();
474e6492875SMatthias Sohn 			}
475e6492875SMatthias Sohn 
476767be14fSRobin Rosenberg 			writeTrashFile(filename, contents);
477767be14fSRobin Rosenberg 			git.add().addFilepattern(filename).call();
478767be14fSRobin Rosenberg 			RevCommit commit = git.commit()
479767be14fSRobin Rosenberg 					.setMessage(branch + ": " + filename).call();
480e6492875SMatthias Sohn 
481767be14fSRobin Rosenberg 			if (originalBranch != null)
482767be14fSRobin Rosenberg 				git.checkout().setName(originalBranch).call();
483e6492875SMatthias Sohn 			else if (empty)
484e6492875SMatthias Sohn 				git.branchCreate().setName(branch).setStartPoint(commit).call();
485e6492875SMatthias Sohn 
486767be14fSRobin Rosenberg 			return commit;
487c0268f89SCarsten Hammer 		} catch (IOException | GitAPIException e) {
488767be14fSRobin Rosenberg 			throw new RuntimeException(e);
489767be14fSRobin Rosenberg 		}
490767be14fSRobin Rosenberg 	}
491767be14fSRobin Rosenberg 
492a90b75b4SMatthias Sohn 	/**
493a90b75b4SMatthias Sohn 	 * Create <code>DirCacheEntry</code>
494a90b75b4SMatthias Sohn 	 *
495a90b75b4SMatthias Sohn 	 * @param path
496a90b75b4SMatthias Sohn 	 * @param mode
497a90b75b4SMatthias Sohn 	 * @return the DirCacheEntry
498a90b75b4SMatthias Sohn 	 */
createEntry(String path, FileMode mode)4996d370d83SHan-Wen Nienhuys 	protected DirCacheEntry createEntry(String path, FileMode mode) {
500767be14fSRobin Rosenberg 		return createEntry(path, mode, DirCacheEntry.STAGE_0, path);
501767be14fSRobin Rosenberg 	}
502767be14fSRobin Rosenberg 
503a90b75b4SMatthias Sohn 	/**
504a90b75b4SMatthias Sohn 	 * Create <code>DirCacheEntry</code>
505a90b75b4SMatthias Sohn 	 *
506a90b75b4SMatthias Sohn 	 * @param path
507a90b75b4SMatthias Sohn 	 * @param mode
508a90b75b4SMatthias Sohn 	 * @param content
509a90b75b4SMatthias Sohn 	 * @return the DirCacheEntry
510a90b75b4SMatthias Sohn 	 */
createEntry(final String path, final FileMode mode, final String content)511767be14fSRobin Rosenberg 	protected DirCacheEntry createEntry(final String path, final FileMode mode,
512767be14fSRobin Rosenberg 			final String content) {
513767be14fSRobin Rosenberg 		return createEntry(path, mode, DirCacheEntry.STAGE_0, content);
514767be14fSRobin Rosenberg 	}
515767be14fSRobin Rosenberg 
516a90b75b4SMatthias Sohn 	/**
517a90b75b4SMatthias Sohn 	 * Create <code>DirCacheEntry</code>
518a90b75b4SMatthias Sohn 	 *
519a90b75b4SMatthias Sohn 	 * @param path
520a90b75b4SMatthias Sohn 	 * @param mode
521a90b75b4SMatthias Sohn 	 * @param stage
522a90b75b4SMatthias Sohn 	 * @param content
523a90b75b4SMatthias Sohn 	 * @return the DirCacheEntry
524a90b75b4SMatthias Sohn 	 */
createEntry(final String path, final FileMode mode, final int stage, final String content)525767be14fSRobin Rosenberg 	protected DirCacheEntry createEntry(final String path, final FileMode mode,
526767be14fSRobin Rosenberg 			final int stage, final String content) {
527767be14fSRobin Rosenberg 		final DirCacheEntry entry = new DirCacheEntry(path, stage);
528767be14fSRobin Rosenberg 		entry.setFileMode(mode);
529b85e9304SDavid Pursehouse 		try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) {
530b85e9304SDavid Pursehouse 			entry.setObjectId(formatter.idFor(
531767be14fSRobin Rosenberg 					Constants.OBJ_BLOB, Constants.encode(content)));
532b85e9304SDavid Pursehouse 		}
533767be14fSRobin Rosenberg 		return entry;
534767be14fSRobin Rosenberg 	}
535068e2df1SRobin Rosenberg 
536a90b75b4SMatthias Sohn 	/**
537e60ea732SDemetr Starshov 	 * Create <code>DirCacheEntry</code>
538e60ea732SDemetr Starshov 	 *
539e60ea732SDemetr Starshov 	 * @param path
540e60ea732SDemetr Starshov 	 * @param objectId
541e60ea732SDemetr Starshov 	 * @return the DirCacheEntry
542e60ea732SDemetr Starshov 	 */
createGitLink(String path, AnyObjectId objectId)543e60ea732SDemetr Starshov 	protected DirCacheEntry createGitLink(String path, AnyObjectId objectId) {
544e60ea732SDemetr Starshov 		final DirCacheEntry entry = new DirCacheEntry(path,
545e60ea732SDemetr Starshov 				DirCacheEntry.STAGE_0);
546e60ea732SDemetr Starshov 		entry.setFileMode(FileMode.GITLINK);
547e60ea732SDemetr Starshov 		entry.setObjectId(objectId);
548e60ea732SDemetr Starshov 		return entry;
549e60ea732SDemetr Starshov 	}
550e60ea732SDemetr Starshov 
551e60ea732SDemetr Starshov 	/**
552a90b75b4SMatthias Sohn 	 * Assert files are equal
553a90b75b4SMatthias Sohn 	 *
554a90b75b4SMatthias Sohn 	 * @param expected
555a90b75b4SMatthias Sohn 	 * @param actual
556a90b75b4SMatthias Sohn 	 * @throws IOException
557a90b75b4SMatthias Sohn 	 */
assertEqualsFile(File expected, File actual)558068e2df1SRobin Rosenberg 	public static void assertEqualsFile(File expected, File actual)
559068e2df1SRobin Rosenberg 			throws IOException {
560068e2df1SRobin Rosenberg 		assertEquals(expected.getCanonicalFile(), actual.getCanonicalFile());
561068e2df1SRobin Rosenberg 	}
562767be14fSRobin Rosenberg }
563