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