1f945c424SShawn O. Pearce /* 25c5f7c6bSMatthias Sohn * Copyright (C) 2009-2010, Google Inc. and others 3f945c424SShawn O. Pearce * 45c5f7c6bSMatthias Sohn * This program and the accompanying materials are made available under the 55c5f7c6bSMatthias Sohn * terms of the Eclipse Distribution License v. 1.0 which is available at 65c5f7c6bSMatthias Sohn * https://www.eclipse.org/org/documents/edl-v10.php. 7f945c424SShawn O. Pearce * 85c5f7c6bSMatthias Sohn * SPDX-License-Identifier: BSD-3-Clause 9f945c424SShawn O. Pearce */ 10f945c424SShawn O. Pearce 11f945c424SShawn O. Pearce package org.eclipse.jgit.junit; 12f945c424SShawn O. Pearce 1330c6c754SDavid Pursehouse import static java.nio.charset.StandardCharsets.UTF_8; 14e875c905SRobin Rosenberg import static org.junit.Assert.assertEquals; 15e875c905SRobin Rosenberg import static org.junit.Assert.fail; 16d9e07a57SRobin Rosenberg 178208da2fSShawn Pearce import java.io.BufferedOutputStream; 18f945c424SShawn O. Pearce import java.io.File; 19f5eb0d93SShawn O. Pearce import java.io.FileOutputStream; 20f945c424SShawn O. Pearce import java.io.IOException; 212156aa89SShawn O. Pearce import java.io.OutputStream; 22f5eb0d93SShawn O. Pearce import java.security.MessageDigest; 23f945c424SShawn O. Pearce import java.util.ArrayList; 24f1a15f7eSDave Borowitz import java.util.Arrays; 25f945c424SShawn O. Pearce import java.util.Collections; 26f945c424SShawn O. Pearce import java.util.Date; 27f5eb0d93SShawn O. Pearce import java.util.HashSet; 28f945c424SShawn O. Pearce import java.util.List; 29f5eb0d93SShawn O. Pearce import java.util.Set; 30f1a15f7eSDave Borowitz import java.util.TimeZone; 31f945c424SShawn O. Pearce 32be08dcb6SDave Borowitz import org.eclipse.jgit.api.Git; 33f945c424SShawn O. Pearce import org.eclipse.jgit.dircache.DirCache; 34f945c424SShawn O. Pearce import org.eclipse.jgit.dircache.DirCacheBuilder; 35f945c424SShawn O. Pearce import org.eclipse.jgit.dircache.DirCacheEditor; 36f945c424SShawn O. Pearce import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath; 37f945c424SShawn O. Pearce import org.eclipse.jgit.dircache.DirCacheEditor.DeleteTree; 38f945c424SShawn O. Pearce import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; 39e875c905SRobin Rosenberg import org.eclipse.jgit.dircache.DirCacheEntry; 40f5eb0d93SShawn O. Pearce import org.eclipse.jgit.errors.IncorrectObjectTypeException; 41f5eb0d93SShawn O. Pearce import org.eclipse.jgit.errors.MissingObjectException; 42f945c424SShawn O. Pearce import org.eclipse.jgit.errors.ObjectWritingException; 43f32b8612SShawn Pearce import org.eclipse.jgit.internal.storage.file.FileRepository; 44f32b8612SShawn Pearce import org.eclipse.jgit.internal.storage.file.LockFile; 45f32b8612SShawn Pearce import org.eclipse.jgit.internal.storage.file.ObjectDirectory; 46efb154fcSNasser Grainawi import org.eclipse.jgit.internal.storage.file.Pack; 47971dafd3SNasser Grainawi import org.eclipse.jgit.internal.storage.file.PackFile; 48f32b8612SShawn Pearce import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry; 49971dafd3SNasser Grainawi import org.eclipse.jgit.internal.storage.pack.PackExt; 50f32b8612SShawn Pearce import org.eclipse.jgit.internal.storage.pack.PackWriter; 51f945c424SShawn O. Pearce import org.eclipse.jgit.lib.AnyObjectId; 52f945c424SShawn O. Pearce import org.eclipse.jgit.lib.Constants; 53f945c424SShawn O. Pearce import org.eclipse.jgit.lib.FileMode; 54f5eb0d93SShawn O. Pearce import org.eclipse.jgit.lib.NullProgressMonitor; 55f5eb0d93SShawn O. Pearce import org.eclipse.jgit.lib.ObjectChecker; 56f945c424SShawn O. Pearce import org.eclipse.jgit.lib.ObjectId; 5788530a17SShawn O. Pearce import org.eclipse.jgit.lib.ObjectInserter; 58f945c424SShawn O. Pearce import org.eclipse.jgit.lib.PersonIdent; 59f945c424SShawn O. Pearce import org.eclipse.jgit.lib.Ref; 60f945c424SShawn O. Pearce import org.eclipse.jgit.lib.RefUpdate; 61f945c424SShawn O. Pearce import org.eclipse.jgit.lib.RefWriter; 62f945c424SShawn O. Pearce import org.eclipse.jgit.lib.Repository; 6322b28569SShawn O. Pearce import org.eclipse.jgit.lib.TagBuilder; 64f1a15f7eSDave Borowitz import org.eclipse.jgit.merge.MergeStrategy; 65f1a15f7eSDave Borowitz import org.eclipse.jgit.merge.ThreeWayMerger; 66f5eb0d93SShawn O. Pearce import org.eclipse.jgit.revwalk.ObjectWalk; 67f945c424SShawn O. Pearce import org.eclipse.jgit.revwalk.RevBlob; 68f945c424SShawn O. Pearce import org.eclipse.jgit.revwalk.RevCommit; 69f945c424SShawn O. Pearce import org.eclipse.jgit.revwalk.RevObject; 70f945c424SShawn O. Pearce import org.eclipse.jgit.revwalk.RevTag; 71f945c424SShawn O. Pearce import org.eclipse.jgit.revwalk.RevTree; 72f945c424SShawn O. Pearce import org.eclipse.jgit.revwalk.RevWalk; 73f945c424SShawn O. Pearce import org.eclipse.jgit.treewalk.TreeWalk; 74f945c424SShawn O. Pearce import org.eclipse.jgit.treewalk.filter.PathFilterGroup; 75c1d40caaSDave Borowitz import org.eclipse.jgit.util.ChangeIdUtil; 7645731756SMatthias Sohn import org.eclipse.jgit.util.FileUtils; 77f945c424SShawn O. Pearce 7889d4a737SShawn O. Pearce /** 7989d4a737SShawn O. Pearce * Wrapper to make creating test data easier. 8089d4a737SShawn O. Pearce * 8189d4a737SShawn O. Pearce * @param <R> 8289d4a737SShawn O. Pearce * type of Repository the test data is stored on. 8389d4a737SShawn O. Pearce */ 848ed59c51SJackson Toeniskoetter public class TestRepository<R extends Repository> implements AutoCloseable { 85f945c424SShawn O. Pearce 86a90b75b4SMatthias Sohn /** Constant <code>AUTHOR="J. Author"</code> */ 8710135580STerry Parker public static final String AUTHOR = "J. Author"; 88f945c424SShawn O. Pearce 89a90b75b4SMatthias Sohn /** Constant <code>AUTHOR_EMAIL="jauthor@example.com"</code> */ 9010135580STerry Parker public static final String AUTHOR_EMAIL = "jauthor@example.com"; 91f945c424SShawn O. Pearce 92a90b75b4SMatthias Sohn /** Constant <code>COMMITTER="J. Committer"</code> */ 9310135580STerry Parker public static final String COMMITTER = "J. Committer"; 94f945c424SShawn O. Pearce 95a90b75b4SMatthias Sohn /** Constant <code>COMMITTER_EMAIL="jcommitter@example.com"</code> */ 9610135580STerry Parker public static final String COMMITTER_EMAIL = "jcommitter@example.com"; 9710135580STerry Parker 9810135580STerry Parker private final PersonIdent defaultAuthor; 9910135580STerry Parker 10010135580STerry Parker private final PersonIdent defaultCommitter; 101f945c424SShawn O. Pearce 10289d4a737SShawn O. Pearce private final R db; 103f945c424SShawn O. Pearce 104be08dcb6SDave Borowitz private final Git git; 105be08dcb6SDave Borowitz 106f945c424SShawn O. Pearce private final RevWalk pool; 107f945c424SShawn O. Pearce 10888530a17SShawn O. Pearce private final ObjectInserter inserter; 109f945c424SShawn O. Pearce 110069040ddSTerry Parker private final MockSystemReader mockSystemReader; 111f945c424SShawn O. Pearce 112f945c424SShawn O. Pearce /** 113f945c424SShawn O. Pearce * Wrap a repository with test building tools. 114f945c424SShawn O. Pearce * 115f945c424SShawn O. Pearce * @param db 116f945c424SShawn O. Pearce * the test repository to write into. 11789d4a737SShawn O. Pearce * @throws IOException 118f945c424SShawn O. Pearce */ TestRepository(R db)11989d4a737SShawn O. Pearce public TestRepository(R db) throws IOException { 120069040ddSTerry Parker this(db, new RevWalk(db), new MockSystemReader()); 121f945c424SShawn O. Pearce } 122f945c424SShawn O. Pearce 123f945c424SShawn O. Pearce /** 124f945c424SShawn O. Pearce * Wrap a repository with test building tools. 125f945c424SShawn O. Pearce * 126f945c424SShawn O. Pearce * @param db 127f945c424SShawn O. Pearce * the test repository to write into. 128f945c424SShawn O. Pearce * @param rw 129f945c424SShawn O. Pearce * the RevObject pool to use for object lookup. 13089d4a737SShawn O. Pearce * @throws IOException 131f945c424SShawn O. Pearce */ TestRepository(R db, RevWalk rw)13289d4a737SShawn O. Pearce public TestRepository(R db, RevWalk rw) throws IOException { 133069040ddSTerry Parker this(db, rw, new MockSystemReader()); 134069040ddSTerry Parker } 135069040ddSTerry Parker 136069040ddSTerry Parker /** 137069040ddSTerry Parker * Wrap a repository with test building tools. 138069040ddSTerry Parker * 139069040ddSTerry Parker * @param db 140069040ddSTerry Parker * the test repository to write into. 141069040ddSTerry Parker * @param rw 142069040ddSTerry Parker * the RevObject pool to use for object lookup. 143069040ddSTerry Parker * @param reader 144069040ddSTerry Parker * the MockSystemReader to use for clock and other system 145069040ddSTerry Parker * operations. 146069040ddSTerry Parker * @throws IOException 1472cdc130dSMatthias Sohn * @since 4.2 148069040ddSTerry Parker */ TestRepository(R db, RevWalk rw, MockSystemReader reader)149069040ddSTerry Parker public TestRepository(R db, RevWalk rw, MockSystemReader reader) 150069040ddSTerry Parker throws IOException { 151f945c424SShawn O. Pearce this.db = db; 152be08dcb6SDave Borowitz this.git = Git.wrap(db); 153f945c424SShawn O. Pearce this.pool = rw; 15488530a17SShawn O. Pearce this.inserter = db.newObjectInserter(); 155069040ddSTerry Parker this.mockSystemReader = reader; 15610135580STerry Parker long now = mockSystemReader.getCurrentTime(); 15710135580STerry Parker int tz = mockSystemReader.getTimezone(now); 15810135580STerry Parker defaultAuthor = new PersonIdent(AUTHOR, AUTHOR_EMAIL, now, tz); 15910135580STerry Parker defaultCommitter = new PersonIdent(COMMITTER, COMMITTER_EMAIL, now, tz); 160f945c424SShawn O. Pearce } 161f945c424SShawn O. Pearce 162a90b75b4SMatthias Sohn /** 163a90b75b4SMatthias Sohn * Get repository 164a90b75b4SMatthias Sohn * 165a90b75b4SMatthias Sohn * @return the repository this helper class operates against. 166a90b75b4SMatthias Sohn */ getRepository()16789d4a737SShawn O. Pearce public R getRepository() { 168f945c424SShawn O. Pearce return db; 169f945c424SShawn O. Pearce } 170f945c424SShawn O. Pearce 171a90b75b4SMatthias Sohn /** 172a90b75b4SMatthias Sohn * Get RevWalk 173a90b75b4SMatthias Sohn * 174a90b75b4SMatthias Sohn * @return get the RevWalk pool all objects are allocated through. 175a90b75b4SMatthias Sohn */ getRevWalk()176f945c424SShawn O. Pearce public RevWalk getRevWalk() { 177f945c424SShawn O. Pearce return pool; 178f945c424SShawn O. Pearce } 179f945c424SShawn O. Pearce 180be08dcb6SDave Borowitz /** 181a90b75b4SMatthias Sohn * Return Git API wrapper 182a90b75b4SMatthias Sohn * 183be08dcb6SDave Borowitz * @return an API wrapper for the underlying repository. This wrapper does 184a90b75b4SMatthias Sohn * not allocate any new resources and need not be closed (but 185a90b75b4SMatthias Sohn * closing it is harmless). 186a90b75b4SMatthias Sohn */ git()187be08dcb6SDave Borowitz public Git git() { 188be08dcb6SDave Borowitz return git; 189be08dcb6SDave Borowitz } 190be08dcb6SDave Borowitz 1912cdc130dSMatthias Sohn /** 192a90b75b4SMatthias Sohn * Get date 193a90b75b4SMatthias Sohn * 1942cdc130dSMatthias Sohn * @return current date. 1952cdc130dSMatthias Sohn * @since 4.2 1962cdc130dSMatthias Sohn */ getDate()197069040ddSTerry Parker public Date getDate() { 198069040ddSTerry Parker return new Date(mockSystemReader.getCurrentTime()); 199f945c424SShawn O. Pearce } 200f945c424SShawn O. Pearce 201a90b75b4SMatthias Sohn /** 202a90b75b4SMatthias Sohn * Get timezone 203a90b75b4SMatthias Sohn * 204a90b75b4SMatthias Sohn * @return timezone used for default identities. 205a90b75b4SMatthias Sohn */ getTimeZone()206f1a15f7eSDave Borowitz public TimeZone getTimeZone() { 207069040ddSTerry Parker return mockSystemReader.getTimeZone(); 208f1a15f7eSDave Borowitz } 209f1a15f7eSDave Borowitz 210f945c424SShawn O. Pearce /** 211f945c424SShawn O. Pearce * Adjust the current time that will used by the next commit. 212f945c424SShawn O. Pearce * 213f945c424SShawn O. Pearce * @param secDelta 214f945c424SShawn O. Pearce * number of seconds to add to the current time. 215f945c424SShawn O. Pearce */ tick(int secDelta)2166d370d83SHan-Wen Nienhuys public void tick(int secDelta) { 217069040ddSTerry Parker mockSystemReader.tick(secDelta); 218f945c424SShawn O. Pearce } 219f945c424SShawn O. Pearce 220f945c424SShawn O. Pearce /** 221069040ddSTerry Parker * Set the author and committer using {@link #getDate()}. 2223e2b9b69SShawn O. Pearce * 2233e2b9b69SShawn O. Pearce * @param c 2243e2b9b69SShawn O. Pearce * the commit builder to store. 2253e2b9b69SShawn O. Pearce */ setAuthorAndCommitter(org.eclipse.jgit.lib.CommitBuilder c)2263e2b9b69SShawn O. Pearce public void setAuthorAndCommitter(org.eclipse.jgit.lib.CommitBuilder c) { 227069040ddSTerry Parker c.setAuthor(new PersonIdent(defaultAuthor, getDate())); 228069040ddSTerry Parker c.setCommitter(new PersonIdent(defaultCommitter, getDate())); 2293e2b9b69SShawn O. Pearce } 2303e2b9b69SShawn O. Pearce 2313e2b9b69SShawn O. Pearce /** 232f945c424SShawn O. Pearce * Create a new blob object in the repository. 233f945c424SShawn O. Pearce * 234f945c424SShawn O. Pearce * @param content 235f945c424SShawn O. Pearce * file content, will be UTF-8 encoded. 236f945c424SShawn O. Pearce * @return reference to the blob. 237f945c424SShawn O. Pearce * @throws Exception 238f945c424SShawn O. Pearce */ blob(String content)2396d370d83SHan-Wen Nienhuys public RevBlob blob(String content) throws Exception { 24030c6c754SDavid Pursehouse return blob(content.getBytes(UTF_8)); 241f945c424SShawn O. Pearce } 242f945c424SShawn O. Pearce 243f945c424SShawn O. Pearce /** 244f945c424SShawn O. Pearce * Create a new blob object in the repository. 245f945c424SShawn O. Pearce * 246f945c424SShawn O. Pearce * @param content 247f945c424SShawn O. Pearce * binary file content. 24800e51e35STerry Parker * @return the new, fully parsed blob. 249f945c424SShawn O. Pearce * @throws Exception 250f945c424SShawn O. Pearce */ blob(byte[] content)2516d370d83SHan-Wen Nienhuys public RevBlob blob(byte[] content) throws Exception { 25288530a17SShawn O. Pearce ObjectId id; 2536599111dSDave Borowitz try (ObjectInserter ins = inserter) { 2546599111dSDave Borowitz id = ins.insert(Constants.OBJ_BLOB, content); 2556599111dSDave Borowitz ins.flush(); 25688530a17SShawn O. Pearce } 25700e51e35STerry Parker return (RevBlob) pool.parseAny(id); 258f945c424SShawn O. Pearce } 259f945c424SShawn O. Pearce 260f945c424SShawn O. Pearce /** 261f945c424SShawn O. Pearce * Construct a regular file mode tree entry. 262f945c424SShawn O. Pearce * 263f945c424SShawn O. Pearce * @param path 264f945c424SShawn O. Pearce * path of the file. 265f945c424SShawn O. Pearce * @param blob 266f945c424SShawn O. Pearce * a blob, previously constructed in the repository. 267f945c424SShawn O. Pearce * @return the entry. 268f945c424SShawn O. Pearce * @throws Exception 269f945c424SShawn O. Pearce */ file(String path, RevBlob blob)2706d370d83SHan-Wen Nienhuys public DirCacheEntry file(String path, RevBlob blob) 271f945c424SShawn O. Pearce throws Exception { 272f945c424SShawn O. Pearce final DirCacheEntry e = new DirCacheEntry(path); 273f945c424SShawn O. Pearce e.setFileMode(FileMode.REGULAR_FILE); 274f945c424SShawn O. Pearce e.setObjectId(blob); 275f945c424SShawn O. Pearce return e; 276f945c424SShawn O. Pearce } 277f945c424SShawn O. Pearce 278f945c424SShawn O. Pearce /** 279f945c424SShawn O. Pearce * Construct a tree from a specific listing of file entries. 280f945c424SShawn O. Pearce * 281f945c424SShawn O. Pearce * @param entries 282f945c424SShawn O. Pearce * the files to include in the tree. The collection does not need 283f945c424SShawn O. Pearce * to be sorted properly and may be empty. 28400e51e35STerry Parker * @return the new, fully parsed tree specified by the entry list. 285f945c424SShawn O. Pearce * @throws Exception 286f945c424SShawn O. Pearce */ tree(DirCacheEntry... entries)2876d370d83SHan-Wen Nienhuys public RevTree tree(DirCacheEntry... entries) throws Exception { 288f945c424SShawn O. Pearce final DirCache dc = DirCache.newInCore(); 289f945c424SShawn O. Pearce final DirCacheBuilder b = dc.builder(); 29000e51e35STerry Parker for (DirCacheEntry e : entries) { 291f945c424SShawn O. Pearce b.add(e); 29200e51e35STerry Parker } 293f945c424SShawn O. Pearce b.finish(); 29488530a17SShawn O. Pearce ObjectId root; 2956599111dSDave Borowitz try (ObjectInserter ins = inserter) { 2966599111dSDave Borowitz root = dc.writeTree(ins); 2976599111dSDave Borowitz ins.flush(); 29888530a17SShawn O. Pearce } 29900e51e35STerry Parker return pool.parseTree(root); 300f945c424SShawn O. Pearce } 301f945c424SShawn O. Pearce 302f945c424SShawn O. Pearce /** 303f945c424SShawn O. Pearce * Lookup an entry stored in a tree, failing if not present. 304f945c424SShawn O. Pearce * 305f945c424SShawn O. Pearce * @param tree 306f945c424SShawn O. Pearce * the tree to search. 307f945c424SShawn O. Pearce * @param path 308f945c424SShawn O. Pearce * the path to find the entry of. 309f945c424SShawn O. Pearce * @return the parsed object entry at this path, never null. 310f945c424SShawn O. Pearce * @throws Exception 311f945c424SShawn O. Pearce */ get(RevTree tree, String path)3126d370d83SHan-Wen Nienhuys public RevObject get(RevTree tree, String path) 313d9e07a57SRobin Rosenberg throws Exception { 3146599111dSDave Borowitz try (TreeWalk tw = new TreeWalk(pool.getObjectReader())) { 315f945c424SShawn O. Pearce tw.setFilter(PathFilterGroup.createFromStrings(Collections 316f945c424SShawn O. Pearce .singleton(path))); 317f945c424SShawn O. Pearce tw.reset(tree); 318f945c424SShawn O. Pearce while (tw.next()) { 319f945c424SShawn O. Pearce if (tw.isSubtree() && !path.equals(tw.getPathString())) { 320f945c424SShawn O. Pearce tw.enterSubtree(); 321f945c424SShawn O. Pearce continue; 322f945c424SShawn O. Pearce } 323f945c424SShawn O. Pearce final ObjectId entid = tw.getObjectId(0); 324f945c424SShawn O. Pearce final FileMode entmode = tw.getFileMode(0); 325f945c424SShawn O. Pearce return pool.lookupAny(entid, entmode.getObjectType()); 326f945c424SShawn O. Pearce } 3276599111dSDave Borowitz } 328d9e07a57SRobin Rosenberg fail("Can't find " + path + " in tree " + tree.name()); 329f945c424SShawn O. Pearce return null; // never reached. 330f945c424SShawn O. Pearce } 331f945c424SShawn O. Pearce 332f945c424SShawn O. Pearce /** 333a80df538SAlex Spradlin * Create a new, unparsed commit. 334a80df538SAlex Spradlin * <p> 335a80df538SAlex Spradlin * See {@link #unparsedCommit(int, RevTree, ObjectId...)}. The tree is the 336a80df538SAlex Spradlin * empty tree (no files or subdirectories). 337a80df538SAlex Spradlin * 338a80df538SAlex Spradlin * @param parents 339a80df538SAlex Spradlin * zero or more IDs of the commit's parents. 340a80df538SAlex Spradlin * @return the ID of the new commit. 341a80df538SAlex Spradlin * @throws Exception 342a80df538SAlex Spradlin */ unparsedCommit(ObjectId... parents)343a80df538SAlex Spradlin public ObjectId unparsedCommit(ObjectId... parents) throws Exception { 344a80df538SAlex Spradlin return unparsedCommit(1, tree(), parents); 345a80df538SAlex Spradlin } 346a80df538SAlex Spradlin 347a80df538SAlex Spradlin /** 348f945c424SShawn O. Pearce * Create a new commit. 349f945c424SShawn O. Pearce * <p> 350f945c424SShawn O. Pearce * See {@link #commit(int, RevTree, RevCommit...)}. The tree is the empty 351f945c424SShawn O. Pearce * tree (no files or subdirectories). 352f945c424SShawn O. Pearce * 353f945c424SShawn O. Pearce * @param parents 354f945c424SShawn O. Pearce * zero or more parents of the commit. 355f945c424SShawn O. Pearce * @return the new commit. 356f945c424SShawn O. Pearce * @throws Exception 357f945c424SShawn O. Pearce */ commit(RevCommit... parents)3586d370d83SHan-Wen Nienhuys public RevCommit commit(RevCommit... parents) throws Exception { 359f945c424SShawn O. Pearce return commit(1, tree(), parents); 360f945c424SShawn O. Pearce } 361f945c424SShawn O. Pearce 362f945c424SShawn O. Pearce /** 363f945c424SShawn O. Pearce * Create a new commit. 364f945c424SShawn O. Pearce * <p> 365f945c424SShawn O. Pearce * See {@link #commit(int, RevTree, RevCommit...)}. 366f945c424SShawn O. Pearce * 367f945c424SShawn O. Pearce * @param tree 368f945c424SShawn O. Pearce * the root tree for the commit. 369f945c424SShawn O. Pearce * @param parents 370f945c424SShawn O. Pearce * zero or more parents of the commit. 371f945c424SShawn O. Pearce * @return the new commit. 372f945c424SShawn O. Pearce * @throws Exception 373f945c424SShawn O. Pearce */ commit(RevTree tree, RevCommit... parents)3746d370d83SHan-Wen Nienhuys public RevCommit commit(RevTree tree, RevCommit... parents) 375f945c424SShawn O. Pearce throws Exception { 376f945c424SShawn O. Pearce return commit(1, tree, parents); 377f945c424SShawn O. Pearce } 378f945c424SShawn O. Pearce 379f945c424SShawn O. Pearce /** 380f945c424SShawn O. Pearce * Create a new commit. 381f945c424SShawn O. Pearce * <p> 382f945c424SShawn O. Pearce * See {@link #commit(int, RevTree, RevCommit...)}. The tree is the empty 383f945c424SShawn O. Pearce * tree (no files or subdirectories). 384f945c424SShawn O. Pearce * 385f945c424SShawn O. Pearce * @param secDelta 386f945c424SShawn O. Pearce * number of seconds to advance {@link #tick(int)} by. 387f945c424SShawn O. Pearce * @param parents 388f945c424SShawn O. Pearce * zero or more parents of the commit. 389f945c424SShawn O. Pearce * @return the new commit. 390f945c424SShawn O. Pearce * @throws Exception 391f945c424SShawn O. Pearce */ commit(int secDelta, RevCommit... parents)3926d370d83SHan-Wen Nienhuys public RevCommit commit(int secDelta, RevCommit... parents) 393f945c424SShawn O. Pearce throws Exception { 394f945c424SShawn O. Pearce return commit(secDelta, tree(), parents); 395f945c424SShawn O. Pearce } 396f945c424SShawn O. Pearce 397f945c424SShawn O. Pearce /** 398f945c424SShawn O. Pearce * Create a new commit. 399f945c424SShawn O. Pearce * <p> 400f945c424SShawn O. Pearce * The author and committer identities are stored using the current 401f945c424SShawn O. Pearce * timestamp, after being incremented by {@code secDelta}. The message body 402f945c424SShawn O. Pearce * is empty. 403f945c424SShawn O. Pearce * 404f945c424SShawn O. Pearce * @param secDelta 405f945c424SShawn O. Pearce * number of seconds to advance {@link #tick(int)} by. 406f945c424SShawn O. Pearce * @param tree 407f945c424SShawn O. Pearce * the root tree for the commit. 408f945c424SShawn O. Pearce * @param parents 409f945c424SShawn O. Pearce * zero or more parents of the commit. 41000e51e35STerry Parker * @return the new, fully parsed commit. 411f945c424SShawn O. Pearce * @throws Exception 412f945c424SShawn O. Pearce */ commit(final int secDelta, final RevTree tree, final RevCommit... parents)413f945c424SShawn O. Pearce public RevCommit commit(final int secDelta, final RevTree tree, 414f945c424SShawn O. Pearce final RevCommit... parents) throws Exception { 415a80df538SAlex Spradlin ObjectId id = unparsedCommit(secDelta, tree, parents); 416a80df538SAlex Spradlin return pool.parseCommit(id); 417a80df538SAlex Spradlin } 418a80df538SAlex Spradlin 419a80df538SAlex Spradlin /** 420a80df538SAlex Spradlin * Create a new, unparsed commit. 421a80df538SAlex Spradlin * <p> 422a80df538SAlex Spradlin * The author and committer identities are stored using the current 423a80df538SAlex Spradlin * timestamp, after being incremented by {@code secDelta}. The message body 424a80df538SAlex Spradlin * is empty. 425a80df538SAlex Spradlin * 426a80df538SAlex Spradlin * @param secDelta 427a80df538SAlex Spradlin * number of seconds to advance {@link #tick(int)} by. 428a80df538SAlex Spradlin * @param tree 429a80df538SAlex Spradlin * the root tree for the commit. 430a80df538SAlex Spradlin * @param parents 431a80df538SAlex Spradlin * zero or more IDs of the commit's parents. 432a80df538SAlex Spradlin * @return the ID of the new commit. 433a80df538SAlex Spradlin * @throws Exception 434a80df538SAlex Spradlin */ unparsedCommit(final int secDelta, final RevTree tree, final ObjectId... parents)435a80df538SAlex Spradlin public ObjectId unparsedCommit(final int secDelta, final RevTree tree, 436a80df538SAlex Spradlin final ObjectId... parents) throws Exception { 437f945c424SShawn O. Pearce tick(secDelta); 438f945c424SShawn O. Pearce 43922b28569SShawn O. Pearce final org.eclipse.jgit.lib.CommitBuilder c; 44022b28569SShawn O. Pearce 44122b28569SShawn O. Pearce c = new org.eclipse.jgit.lib.CommitBuilder(); 442f945c424SShawn O. Pearce c.setTreeId(tree); 443f945c424SShawn O. Pearce c.setParentIds(parents); 444069040ddSTerry Parker c.setAuthor(new PersonIdent(defaultAuthor, getDate())); 445069040ddSTerry Parker c.setCommitter(new PersonIdent(defaultCommitter, getDate())); 446f945c424SShawn O. Pearce c.setMessage(""); 44788530a17SShawn O. Pearce ObjectId id; 4486599111dSDave Borowitz try (ObjectInserter ins = inserter) { 4496599111dSDave Borowitz id = ins.insert(c); 4506599111dSDave Borowitz ins.flush(); 45188530a17SShawn O. Pearce } 452a80df538SAlex Spradlin return id; 453f945c424SShawn O. Pearce } 454f945c424SShawn O. Pearce 455a90b75b4SMatthias Sohn /** 456a90b75b4SMatthias Sohn * Create commit builder 457a90b75b4SMatthias Sohn * 458a90b75b4SMatthias Sohn * @return a new commit builder. 459a90b75b4SMatthias Sohn */ commit()460f945c424SShawn O. Pearce public CommitBuilder commit() { 461f945c424SShawn O. Pearce return new CommitBuilder(); 462f945c424SShawn O. Pearce } 463f945c424SShawn O. Pearce 464f945c424SShawn O. Pearce /** 465f945c424SShawn O. Pearce * Construct an annotated tag object pointing at another object. 466f945c424SShawn O. Pearce * <p> 467f945c424SShawn O. Pearce * The tagger is the committer identity, at the current time as specified by 468f945c424SShawn O. Pearce * {@link #tick(int)}. The time is not increased. 469f945c424SShawn O. Pearce * <p> 470f945c424SShawn O. Pearce * The tag message is empty. 471f945c424SShawn O. Pearce * 472f945c424SShawn O. Pearce * @param name 473f945c424SShawn O. Pearce * name of the tag. Traditionally a tag name should not start 474f945c424SShawn O. Pearce * with {@code refs/tags/}. 475f945c424SShawn O. Pearce * @param dst 476f945c424SShawn O. Pearce * object the tag should be pointed at. 47700e51e35STerry Parker * @return the new, fully parsed annotated tag object. 478f945c424SShawn O. Pearce * @throws Exception 479f945c424SShawn O. Pearce */ tag(String name, RevObject dst)4806d370d83SHan-Wen Nienhuys public RevTag tag(String name, RevObject dst) throws Exception { 48122b28569SShawn O. Pearce final TagBuilder t = new TagBuilder(); 482707912b3SShawn O. Pearce t.setObjectId(dst); 483f945c424SShawn O. Pearce t.setTag(name); 484069040ddSTerry Parker t.setTagger(new PersonIdent(defaultCommitter, getDate())); 485f945c424SShawn O. Pearce t.setMessage(""); 48688530a17SShawn O. Pearce ObjectId id; 4876599111dSDave Borowitz try (ObjectInserter ins = inserter) { 4886599111dSDave Borowitz id = ins.insert(t); 4896599111dSDave Borowitz ins.flush(); 49088530a17SShawn O. Pearce } 49100e51e35STerry Parker return pool.parseTag(id); 492f945c424SShawn O. Pearce } 493f945c424SShawn O. Pearce 494f945c424SShawn O. Pearce /** 495f945c424SShawn O. Pearce * Update a reference to point to an object. 496f945c424SShawn O. Pearce * 497f945c424SShawn O. Pearce * @param ref 498f945c424SShawn O. Pearce * the name of the reference to update to. If {@code ref} does 499f945c424SShawn O. Pearce * not start with {@code refs/} and is not the magic names 500f945c424SShawn O. Pearce * {@code HEAD} {@code FETCH_HEAD} or {@code MERGE_HEAD}, then 501f945c424SShawn O. Pearce * {@code refs/heads/} will be prefixed in front of the given 502f945c424SShawn O. Pearce * name, thereby assuming it is a branch. 503f945c424SShawn O. Pearce * @param to 504f945c424SShawn O. Pearce * the target object. 505f945c424SShawn O. Pearce * @return the target object. 506f945c424SShawn O. Pearce * @throws Exception 507f945c424SShawn O. Pearce */ update(String ref, CommitBuilder to)508f945c424SShawn O. Pearce public RevCommit update(String ref, CommitBuilder to) throws Exception { 509f945c424SShawn O. Pearce return update(ref, to.create()); 510f945c424SShawn O. Pearce } 511f945c424SShawn O. Pearce 512f945c424SShawn O. Pearce /** 513da85ca73SDave Borowitz * Amend an existing ref. 514da85ca73SDave Borowitz * 515da85ca73SDave Borowitz * @param ref 516da85ca73SDave Borowitz * the name of the reference to amend, which must already exist. 517da85ca73SDave Borowitz * If {@code ref} does not start with {@code refs/} and is not the 518da85ca73SDave Borowitz * magic names {@code HEAD} {@code FETCH_HEAD} or {@code 519da85ca73SDave Borowitz * MERGE_HEAD}, then {@code refs/heads/} will be prefixed in front 520da85ca73SDave Borowitz * of the given name, thereby assuming it is a branch. 521da85ca73SDave Borowitz * @return commit builder that amends the branch on commit. 522da85ca73SDave Borowitz * @throws Exception 523da85ca73SDave Borowitz */ amendRef(String ref)524da85ca73SDave Borowitz public CommitBuilder amendRef(String ref) throws Exception { 525da85ca73SDave Borowitz String name = normalizeRef(ref); 5264c236ff4SMatthias Sohn Ref r = db.exactRef(name); 527da85ca73SDave Borowitz if (r == null) 528da85ca73SDave Borowitz throw new IOException("Not a ref: " + ref); 529da85ca73SDave Borowitz return amend(pool.parseCommit(r.getObjectId()), branch(name).commit()); 530da85ca73SDave Borowitz } 531da85ca73SDave Borowitz 532da85ca73SDave Borowitz /** 533da85ca73SDave Borowitz * Amend an existing commit. 534da85ca73SDave Borowitz * 535da85ca73SDave Borowitz * @param id 536da85ca73SDave Borowitz * the id of the commit to amend. 537da85ca73SDave Borowitz * @return commit builder. 538da85ca73SDave Borowitz * @throws Exception 539da85ca73SDave Borowitz */ amend(AnyObjectId id)540da85ca73SDave Borowitz public CommitBuilder amend(AnyObjectId id) throws Exception { 541da85ca73SDave Borowitz return amend(pool.parseCommit(id), commit()); 542da85ca73SDave Borowitz } 543da85ca73SDave Borowitz amend(RevCommit old, CommitBuilder b)544da85ca73SDave Borowitz private CommitBuilder amend(RevCommit old, CommitBuilder b) throws Exception { 545da85ca73SDave Borowitz pool.parseBody(old); 546da85ca73SDave Borowitz b.author(old.getAuthorIdent()); 547da85ca73SDave Borowitz b.committer(old.getCommitterIdent()); 548da85ca73SDave Borowitz b.message(old.getFullMessage()); 549da85ca73SDave Borowitz // Use the committer name from the old commit, but update it after ticking 550da85ca73SDave Borowitz // the clock in CommitBuilder#create(). 551da85ca73SDave Borowitz b.updateCommitterTime = true; 552da85ca73SDave Borowitz 553da85ca73SDave Borowitz // Reset parents to original parents. 554da85ca73SDave Borowitz b.noParents(); 555da85ca73SDave Borowitz for (int i = 0; i < old.getParentCount(); i++) 556da85ca73SDave Borowitz b.parent(old.getParent(i)); 557da85ca73SDave Borowitz 558da85ca73SDave Borowitz // Reset tree to original tree; resetting parents reset tree contents to the 559da85ca73SDave Borowitz // first parent. 560da85ca73SDave Borowitz b.tree.clear(); 561da85ca73SDave Borowitz try (TreeWalk tw = new TreeWalk(db)) { 562da85ca73SDave Borowitz tw.reset(old.getTree()); 563da85ca73SDave Borowitz tw.setRecursive(true); 564da85ca73SDave Borowitz while (tw.next()) { 565da85ca73SDave Borowitz b.edit(new PathEdit(tw.getPathString()) { 566da85ca73SDave Borowitz @Override 567da85ca73SDave Borowitz public void apply(DirCacheEntry ent) { 568da85ca73SDave Borowitz ent.setFileMode(tw.getFileMode(0)); 569da85ca73SDave Borowitz ent.setObjectId(tw.getObjectId(0)); 570da85ca73SDave Borowitz } 571da85ca73SDave Borowitz }); 572da85ca73SDave Borowitz } 573da85ca73SDave Borowitz } 574da85ca73SDave Borowitz 575da85ca73SDave Borowitz return b; 576da85ca73SDave Borowitz } 577da85ca73SDave Borowitz 578da85ca73SDave Borowitz /** 579f945c424SShawn O. Pearce * Update a reference to point to an object. 580f945c424SShawn O. Pearce * 581f945c424SShawn O. Pearce * @param <T> 582f945c424SShawn O. Pearce * type of the target object. 583f945c424SShawn O. Pearce * @param ref 584f945c424SShawn O. Pearce * the name of the reference to update to. If {@code ref} does 585f945c424SShawn O. Pearce * not start with {@code refs/} and is not the magic names 586f945c424SShawn O. Pearce * {@code HEAD} {@code FETCH_HEAD} or {@code MERGE_HEAD}, then 587f945c424SShawn O. Pearce * {@code refs/heads/} will be prefixed in front of the given 588f945c424SShawn O. Pearce * name, thereby assuming it is a branch. 589f945c424SShawn O. Pearce * @param obj 590f945c424SShawn O. Pearce * the target object. 591f945c424SShawn O. Pearce * @return the target object. 592f945c424SShawn O. Pearce * @throws Exception 593f945c424SShawn O. Pearce */ update(String ref, T obj)594f945c424SShawn O. Pearce public <T extends AnyObjectId> T update(String ref, T obj) throws Exception { 595da85ca73SDave Borowitz ref = normalizeRef(ref); 596f945c424SShawn O. Pearce RefUpdate u = db.updateRef(ref); 597f945c424SShawn O. Pearce u.setNewObjectId(obj); 598f945c424SShawn O. Pearce switch (u.forceUpdate()) { 599f945c424SShawn O. Pearce case FAST_FORWARD: 600f945c424SShawn O. Pearce case FORCED: 601f945c424SShawn O. Pearce case NEW: 602f945c424SShawn O. Pearce case NO_CHANGE: 603f945c424SShawn O. Pearce updateServerInfo(); 604f945c424SShawn O. Pearce return obj; 605f945c424SShawn O. Pearce 606f945c424SShawn O. Pearce default: 607f945c424SShawn O. Pearce throw new IOException("Cannot write " + ref + " " + u.getResult()); 608f945c424SShawn O. Pearce } 609f945c424SShawn O. Pearce } 610f945c424SShawn O. Pearce 611bc9df9c6SJonathan Nieder /** 612bc9df9c6SJonathan Nieder * Delete a reference. 613bc9df9c6SJonathan Nieder * 614bc9df9c6SJonathan Nieder * @param ref 615bc9df9c6SJonathan Nieder * the name of the reference to delete. This is normalized 6160c72f60fSMatthias Sohn * in the same way as {@link #update(String, AnyObjectId)}. 617bc9df9c6SJonathan Nieder * @throws Exception 618bc9df9c6SJonathan Nieder * @since 4.4 619bc9df9c6SJonathan Nieder */ delete(String ref)620bc9df9c6SJonathan Nieder public void delete(String ref) throws Exception { 621bc9df9c6SJonathan Nieder ref = normalizeRef(ref); 622bc9df9c6SJonathan Nieder RefUpdate u = db.updateRef(ref); 62315a189e4SMinh Thai u.setForceUpdate(true); 624bc9df9c6SJonathan Nieder switch (u.delete()) { 625bc9df9c6SJonathan Nieder case FAST_FORWARD: 626bc9df9c6SJonathan Nieder case FORCED: 627bc9df9c6SJonathan Nieder case NEW: 628bc9df9c6SJonathan Nieder case NO_CHANGE: 629bc9df9c6SJonathan Nieder updateServerInfo(); 630bc9df9c6SJonathan Nieder return; 631bc9df9c6SJonathan Nieder 632bc9df9c6SJonathan Nieder default: 633bc9df9c6SJonathan Nieder throw new IOException("Cannot delete " + ref + " " + u.getResult()); 634bc9df9c6SJonathan Nieder } 635bc9df9c6SJonathan Nieder } 636bc9df9c6SJonathan Nieder normalizeRef(String ref)637da85ca73SDave Borowitz private static String normalizeRef(String ref) { 638da85ca73SDave Borowitz if (Constants.HEAD.equals(ref)) { 639da85ca73SDave Borowitz // nothing 640da85ca73SDave Borowitz } else if ("FETCH_HEAD".equals(ref)) { 641da85ca73SDave Borowitz // nothing 642da85ca73SDave Borowitz } else if ("MERGE_HEAD".equals(ref)) { 643da85ca73SDave Borowitz // nothing 644da85ca73SDave Borowitz } else if (ref.startsWith(Constants.R_REFS)) { 645da85ca73SDave Borowitz // nothing 646da85ca73SDave Borowitz } else 647da85ca73SDave Borowitz ref = Constants.R_HEADS + ref; 648da85ca73SDave Borowitz return ref; 649da85ca73SDave Borowitz } 650da85ca73SDave Borowitz 651f945c424SShawn O. Pearce /** 652d79cadb3SDave Borowitz * Soft-reset HEAD to a detached state. 653a90b75b4SMatthias Sohn * 654d79cadb3SDave Borowitz * @param id 655d79cadb3SDave Borowitz * ID of detached head. 656d79cadb3SDave Borowitz * @throws Exception 657d79cadb3SDave Borowitz * @see #reset(String) 658d79cadb3SDave Borowitz */ reset(AnyObjectId id)659d79cadb3SDave Borowitz public void reset(AnyObjectId id) throws Exception { 660d79cadb3SDave Borowitz RefUpdate ru = db.updateRef(Constants.HEAD, true); 661d79cadb3SDave Borowitz ru.setNewObjectId(id); 662d79cadb3SDave Borowitz RefUpdate.Result result = ru.forceUpdate(); 663d79cadb3SDave Borowitz switch (result) { 664d79cadb3SDave Borowitz case FAST_FORWARD: 665d79cadb3SDave Borowitz case FORCED: 666d79cadb3SDave Borowitz case NEW: 667d79cadb3SDave Borowitz case NO_CHANGE: 668d79cadb3SDave Borowitz break; 669d79cadb3SDave Borowitz default: 670d79cadb3SDave Borowitz throw new IOException(String.format( 671d79cadb3SDave Borowitz "Checkout \"%s\" failed: %s", id.name(), result)); 672d79cadb3SDave Borowitz } 673d79cadb3SDave Borowitz } 674d79cadb3SDave Borowitz 675d79cadb3SDave Borowitz /** 676d79cadb3SDave Borowitz * Soft-reset HEAD to a different commit. 677d79cadb3SDave Borowitz * <p> 678d79cadb3SDave Borowitz * This is equivalent to {@code git reset --soft} in that it modifies HEAD but 679d79cadb3SDave Borowitz * not the index or the working tree of a non-bare repository. 680d79cadb3SDave Borowitz * 681d79cadb3SDave Borowitz * @param name 682d79cadb3SDave Borowitz * revision string; either an existing ref name, or something that 683d79cadb3SDave Borowitz * can be parsed to an object ID. 684d79cadb3SDave Borowitz * @throws Exception 685d79cadb3SDave Borowitz */ reset(String name)686d79cadb3SDave Borowitz public void reset(String name) throws Exception { 687d79cadb3SDave Borowitz RefUpdate.Result result; 688d79cadb3SDave Borowitz ObjectId id = db.resolve(name); 689d79cadb3SDave Borowitz if (id == null) 690d79cadb3SDave Borowitz throw new IOException("Not a revision: " + name); 691d79cadb3SDave Borowitz RefUpdate ru = db.updateRef(Constants.HEAD, false); 692d79cadb3SDave Borowitz ru.setNewObjectId(id); 693d79cadb3SDave Borowitz result = ru.forceUpdate(); 694d79cadb3SDave Borowitz switch (result) { 695d79cadb3SDave Borowitz case FAST_FORWARD: 696d79cadb3SDave Borowitz case FORCED: 697d79cadb3SDave Borowitz case NEW: 698d79cadb3SDave Borowitz case NO_CHANGE: 699d79cadb3SDave Borowitz break; 700d79cadb3SDave Borowitz default: 701d79cadb3SDave Borowitz throw new IOException(String.format( 702d79cadb3SDave Borowitz "Checkout \"%s\" failed: %s", name, result)); 703d79cadb3SDave Borowitz } 704d79cadb3SDave Borowitz } 705d79cadb3SDave Borowitz 706d79cadb3SDave Borowitz /** 707f1a15f7eSDave Borowitz * Cherry-pick a commit onto HEAD. 708f1a15f7eSDave Borowitz * <p> 709f1a15f7eSDave Borowitz * This differs from {@code git cherry-pick} in that it works in a bare 710f1a15f7eSDave Borowitz * repository. As a result, any merge failure results in an exception, as 711f1a15f7eSDave Borowitz * there is no way to recover. 712f1a15f7eSDave Borowitz * 713f1a15f7eSDave Borowitz * @param id 714f1a15f7eSDave Borowitz * commit-ish to cherry-pick. 71500e51e35STerry Parker * @return the new, fully parsed commit, or null if no work was done due to 71600e51e35STerry Parker * the resulting tree being identical. 717f1a15f7eSDave Borowitz * @throws Exception 718f1a15f7eSDave Borowitz */ cherryPick(AnyObjectId id)719f1a15f7eSDave Borowitz public RevCommit cherryPick(AnyObjectId id) throws Exception { 720f1a15f7eSDave Borowitz RevCommit commit = pool.parseCommit(id); 721f1a15f7eSDave Borowitz pool.parseBody(commit); 722f1a15f7eSDave Borowitz if (commit.getParentCount() != 1) 723f1a15f7eSDave Borowitz throw new IOException(String.format( 724f1a15f7eSDave Borowitz "Expected 1 parent for %s, found: %s", 725f1a15f7eSDave Borowitz id.name(), Arrays.asList(commit.getParents()))); 726f1a15f7eSDave Borowitz RevCommit parent = commit.getParent(0); 727f1a15f7eSDave Borowitz pool.parseHeaders(parent); 728f1a15f7eSDave Borowitz 7294c236ff4SMatthias Sohn Ref headRef = db.exactRef(Constants.HEAD); 730f1a15f7eSDave Borowitz if (headRef == null) 731f1a15f7eSDave Borowitz throw new IOException("Missing HEAD"); 732f1a15f7eSDave Borowitz RevCommit head = pool.parseCommit(headRef.getObjectId()); 733f1a15f7eSDave Borowitz 734f1a15f7eSDave Borowitz ThreeWayMerger merger = MergeStrategy.RECURSIVE.newMerger(db, true); 735f1a15f7eSDave Borowitz merger.setBase(parent.getTree()); 736f1a15f7eSDave Borowitz if (merger.merge(head, commit)) { 7378f7e8513SMatthias Sohn if (AnyObjectId.isEqual(head.getTree(), merger.getResultTreeId())) 738f1a15f7eSDave Borowitz return null; 739f1a15f7eSDave Borowitz tick(1); 740f1a15f7eSDave Borowitz org.eclipse.jgit.lib.CommitBuilder b = 741f1a15f7eSDave Borowitz new org.eclipse.jgit.lib.CommitBuilder(); 742f1a15f7eSDave Borowitz b.setParentId(head); 743f1a15f7eSDave Borowitz b.setTreeId(merger.getResultTreeId()); 744f1a15f7eSDave Borowitz b.setAuthor(commit.getAuthorIdent()); 745069040ddSTerry Parker b.setCommitter(new PersonIdent(defaultCommitter, getDate())); 746f1a15f7eSDave Borowitz b.setMessage(commit.getFullMessage()); 747f1a15f7eSDave Borowitz ObjectId result; 748f1a15f7eSDave Borowitz try (ObjectInserter ins = inserter) { 749f1a15f7eSDave Borowitz result = ins.insert(b); 750f1a15f7eSDave Borowitz ins.flush(); 751f1a15f7eSDave Borowitz } 752f1a15f7eSDave Borowitz update(Constants.HEAD, result); 753f1a15f7eSDave Borowitz return pool.parseCommit(result); 754f1a15f7eSDave Borowitz } 75598cdca9bSDavid Pursehouse throw new IOException("Merge conflict"); 756f1a15f7eSDave Borowitz } 757f1a15f7eSDave Borowitz 758f1a15f7eSDave Borowitz /** 759f945c424SShawn O. Pearce * Update the dumb client server info files. 760f945c424SShawn O. Pearce * 761f945c424SShawn O. Pearce * @throws Exception 762f945c424SShawn O. Pearce */ updateServerInfo()763f945c424SShawn O. Pearce public void updateServerInfo() throws Exception { 76489d4a737SShawn O. Pearce if (db instanceof FileRepository) { 76589d4a737SShawn O. Pearce final FileRepository fr = (FileRepository) db; 7661f6d43a6SDavid Pursehouse RefWriter rw = new RefWriter(fr.getRefDatabase().getRefs()) { 767f945c424SShawn O. Pearce @Override 7686d370d83SHan-Wen Nienhuys protected void writeFile(String name, byte[] bin) 769f945c424SShawn O. Pearce throws IOException { 77089d4a737SShawn O. Pearce File path = new File(fr.getDirectory(), name); 77189d4a737SShawn O. Pearce TestRepository.this.writeFile(path, bin); 772f945c424SShawn O. Pearce } 773f945c424SShawn O. Pearce }; 774f945c424SShawn O. Pearce rw.writePackedRefs(); 775f945c424SShawn O. Pearce rw.writeInfoRefs(); 776f5eb0d93SShawn O. Pearce 777f5eb0d93SShawn O. Pearce final StringBuilder w = new StringBuilder(); 778efb154fcSNasser Grainawi for (Pack p : fr.getObjectDatabase().getPacks()) { 779f5eb0d93SShawn O. Pearce w.append("P "); 780f5eb0d93SShawn O. Pearce w.append(p.getPackFile().getName()); 781f5eb0d93SShawn O. Pearce w.append('\n'); 782f5eb0d93SShawn O. Pearce } 78389d4a737SShawn O. Pearce writeFile(new File(new File(fr.getObjectDatabase().getDirectory(), 78489d4a737SShawn O. Pearce "info"), "packs"), Constants.encodeASCII(w.toString())); 785f945c424SShawn O. Pearce } 786f945c424SShawn O. Pearce } 787f945c424SShawn O. Pearce 788f945c424SShawn O. Pearce /** 789f945c424SShawn O. Pearce * Ensure the body of the given object has been parsed. 790f945c424SShawn O. Pearce * 791f945c424SShawn O. Pearce * @param <T> 792a90b75b4SMatthias Sohn * type of object, e.g. {@link org.eclipse.jgit.revwalk.RevTag} 793a90b75b4SMatthias Sohn * or {@link org.eclipse.jgit.revwalk.RevCommit}. 794f945c424SShawn O. Pearce * @param object 795f945c424SShawn O. Pearce * reference to the (possibly unparsed) object to force body 796f945c424SShawn O. Pearce * parsing of. 797f945c424SShawn O. Pearce * @return {@code object} 798f945c424SShawn O. Pearce * @throws Exception 799f945c424SShawn O. Pearce */ parseBody(T object)8006d370d83SHan-Wen Nienhuys public <T extends RevObject> T parseBody(T object) throws Exception { 801f945c424SShawn O. Pearce pool.parseBody(object); 802f945c424SShawn O. Pearce return object; 803f945c424SShawn O. Pearce } 804f945c424SShawn O. Pearce 805f945c424SShawn O. Pearce /** 806f945c424SShawn O. Pearce * Create a new branch builder for this repository. 807f945c424SShawn O. Pearce * 808f945c424SShawn O. Pearce * @param ref 809f945c424SShawn O. Pearce * name of the branch to be constructed. If {@code ref} does not 810f945c424SShawn O. Pearce * start with {@code refs/} the prefix {@code refs/heads/} will 811f945c424SShawn O. Pearce * be added. 812f945c424SShawn O. Pearce * @return builder for the named branch. 813f945c424SShawn O. Pearce */ branch(String ref)814f945c424SShawn O. Pearce public BranchBuilder branch(String ref) { 815f945c424SShawn O. Pearce if (Constants.HEAD.equals(ref)) { 816e2513558SRobin Rosenberg // nothing 817f945c424SShawn O. Pearce } else if (ref.startsWith(Constants.R_REFS)) { 818e2513558SRobin Rosenberg // nothing 819f945c424SShawn O. Pearce } else 820f945c424SShawn O. Pearce ref = Constants.R_HEADS + ref; 821f945c424SShawn O. Pearce return new BranchBuilder(ref); 822f945c424SShawn O. Pearce } 823f945c424SShawn O. Pearce 824f5eb0d93SShawn O. Pearce /** 825a5514932SSasa Zivkov * Tag an object using a lightweight tag. 826a5514932SSasa Zivkov * 827a5514932SSasa Zivkov * @param name 828a5514932SSasa Zivkov * the tag name. The /refs/tags/ prefix will be added if the name 829a5514932SSasa Zivkov * doesn't start with it 830a5514932SSasa Zivkov * @param obj 831a5514932SSasa Zivkov * the object to tag 832a5514932SSasa Zivkov * @return the tagged object 833a5514932SSasa Zivkov * @throws Exception 834a5514932SSasa Zivkov */ lightweightTag(String name, ObjectId obj)835a5514932SSasa Zivkov public ObjectId lightweightTag(String name, ObjectId obj) throws Exception { 836a5514932SSasa Zivkov if (!name.startsWith(Constants.R_TAGS)) 837a5514932SSasa Zivkov name = Constants.R_TAGS + name; 838a5514932SSasa Zivkov return update(name, obj); 839a5514932SSasa Zivkov } 840a5514932SSasa Zivkov 841a5514932SSasa Zivkov /** 842f5eb0d93SShawn O. Pearce * Run consistency checks against the object database. 843f5eb0d93SShawn O. Pearce * <p> 844f5eb0d93SShawn O. Pearce * This method completes silently if the checks pass. A temporary revision 845f5eb0d93SShawn O. Pearce * pool is constructed during the checking. 846f5eb0d93SShawn O. Pearce * 847f5eb0d93SShawn O. Pearce * @param tips 848f5eb0d93SShawn O. Pearce * the tips to start checking from; if not supplied the refs of 849f5eb0d93SShawn O. Pearce * the repository are used instead. 850f5eb0d93SShawn O. Pearce * @throws MissingObjectException 851f5eb0d93SShawn O. Pearce * @throws IncorrectObjectTypeException 852f5eb0d93SShawn O. Pearce * @throws IOException 853f5eb0d93SShawn O. Pearce */ fsck(RevObject... tips)854f5eb0d93SShawn O. Pearce public void fsck(RevObject... tips) throws MissingObjectException, 855f5eb0d93SShawn O. Pearce IncorrectObjectTypeException, IOException { 8566599111dSDave Borowitz try (ObjectWalk ow = new ObjectWalk(db)) { 857f5eb0d93SShawn O. Pearce if (tips.length != 0) { 858f5eb0d93SShawn O. Pearce for (RevObject o : tips) 859f5eb0d93SShawn O. Pearce ow.markStart(ow.parseAny(o)); 860f5eb0d93SShawn O. Pearce } else { 8611f6d43a6SDavid Pursehouse for (Ref r : db.getRefDatabase().getRefs()) 862f5eb0d93SShawn O. Pearce ow.markStart(ow.parseAny(r.getObjectId())); 863f5eb0d93SShawn O. Pearce } 864f5eb0d93SShawn O. Pearce 865f5eb0d93SShawn O. Pearce ObjectChecker oc = new ObjectChecker(); 866f5eb0d93SShawn O. Pearce for (;;) { 867f5eb0d93SShawn O. Pearce final RevCommit o = ow.next(); 868f5eb0d93SShawn O. Pearce if (o == null) 869f5eb0d93SShawn O. Pearce break; 870f5eb0d93SShawn O. Pearce 871acb7be2cSShawn O. Pearce final byte[] bin = db.open(o, o.getType()).getCachedBytes(); 872fa7ce0e0SShawn Pearce oc.checkCommit(o, bin); 873f5eb0d93SShawn O. Pearce assertHash(o, bin); 874f5eb0d93SShawn O. Pearce } 875f5eb0d93SShawn O. Pearce 876f5eb0d93SShawn O. Pearce for (;;) { 877f5eb0d93SShawn O. Pearce final RevObject o = ow.nextObject(); 878f5eb0d93SShawn O. Pearce if (o == null) 879f5eb0d93SShawn O. Pearce break; 880f5eb0d93SShawn O. Pearce 881acb7be2cSShawn O. Pearce final byte[] bin = db.open(o, o.getType()).getCachedBytes(); 882fa7ce0e0SShawn Pearce oc.check(o, o.getType(), bin); 883f5eb0d93SShawn O. Pearce assertHash(o, bin); 884f5eb0d93SShawn O. Pearce } 885f5eb0d93SShawn O. Pearce } 8866599111dSDave Borowitz } 887f5eb0d93SShawn O. Pearce assertHash(RevObject id, byte[] bin)888f5eb0d93SShawn O. Pearce private static void assertHash(RevObject id, byte[] bin) { 889f5eb0d93SShawn O. Pearce MessageDigest md = Constants.newMessageDigest(); 890f5eb0d93SShawn O. Pearce md.update(Constants.encodedTypeString(id.getType())); 891f5eb0d93SShawn O. Pearce md.update((byte) ' '); 892f5eb0d93SShawn O. Pearce md.update(Constants.encodeASCII(bin.length)); 893f5eb0d93SShawn O. Pearce md.update((byte) 0); 894f5eb0d93SShawn O. Pearce md.update(bin); 895d9e07a57SRobin Rosenberg assertEquals(id, ObjectId.fromRaw(md.digest())); 896f5eb0d93SShawn O. Pearce } 897f5eb0d93SShawn O. Pearce 898f5eb0d93SShawn O. Pearce /** 899f5eb0d93SShawn O. Pearce * Pack all reachable objects in the repository into a single pack file. 900f5eb0d93SShawn O. Pearce * <p> 901f5eb0d93SShawn O. Pearce * All loose objects are automatically pruned. Existing packs however are 902f5eb0d93SShawn O. Pearce * not removed. 903f5eb0d93SShawn O. Pearce * 904f5eb0d93SShawn O. Pearce * @throws Exception 905f5eb0d93SShawn O. Pearce */ packAndPrune()906f5eb0d93SShawn O. Pearce public void packAndPrune() throws Exception { 90789d4a737SShawn O. Pearce if (db.getObjectDatabase() instanceof ObjectDirectory) { 90889d4a737SShawn O. Pearce ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase(); 9096b62e53bSShawn O. Pearce NullProgressMonitor m = NullProgressMonitor.INSTANCE; 910f5eb0d93SShawn O. Pearce 911971dafd3SNasser Grainawi final PackFile pack, idx; 9126599111dSDave Borowitz try (PackWriter pw = new PackWriter(db)) { 9133b444863SDavid Pursehouse Set<ObjectId> all = new HashSet<>(); 9141f6d43a6SDavid Pursehouse for (Ref r : db.getRefDatabase().getRefs()) 915f5eb0d93SShawn O. Pearce all.add(r.getObjectId()); 9164e650c0dSShawn Pearce pw.preparePack(m, all, PackWriter.NONE); 917f5eb0d93SShawn O. Pearce 918*2a6b2eddSNasser Grainawi pack = new PackFile(odb.getPackDirectory(), pw.computeName(), 919*2a6b2eddSNasser Grainawi PackExt.PACK); 9206599111dSDave Borowitz try (OutputStream out = 9218208da2fSShawn Pearce new BufferedOutputStream(new FileOutputStream(pack))) { 9226b62e53bSShawn O. Pearce pw.writePack(m, m, out); 923f5eb0d93SShawn O. Pearce } 924f5eb0d93SShawn O. Pearce pack.setReadOnly(); 925f5eb0d93SShawn O. Pearce 926971dafd3SNasser Grainawi idx = pack.create(PackExt.INDEX); 9276599111dSDave Borowitz try (OutputStream out = 9288208da2fSShawn Pearce new BufferedOutputStream(new FileOutputStream(idx))) { 929f5eb0d93SShawn O. Pearce pw.writeIndex(out); 930f5eb0d93SShawn O. Pearce } 931f5eb0d93SShawn O. Pearce idx.setReadOnly(); 932a45728d7SShawn O. Pearce } 933f5eb0d93SShawn O. Pearce 93482ecfb3eSColby Ranger odb.openPack(pack); 935f5eb0d93SShawn O. Pearce updateServerInfo(); 936f5eb0d93SShawn O. Pearce prunePacked(odb); 937f5eb0d93SShawn O. Pearce } 93889d4a737SShawn O. Pearce } 939f5eb0d93SShawn O. Pearce 9408ed59c51SJackson Toeniskoetter /** 9418ed59c51SJackson Toeniskoetter * Closes the underlying {@link Repository} object and any other internal 9428ed59c51SJackson Toeniskoetter * resources. 9438ed59c51SJackson Toeniskoetter * <p> 9448ed59c51SJackson Toeniskoetter * {@link AutoCloseable} resources that may escape this object, such as 9458ed59c51SJackson Toeniskoetter * those returned by the {@link #git} and {@link #getRevWalk()} methods are 9468ed59c51SJackson Toeniskoetter * not closed. 9478ed59c51SJackson Toeniskoetter */ 9488ed59c51SJackson Toeniskoetter @Override close()9498ed59c51SJackson Toeniskoetter public void close() { 9508ed59c51SJackson Toeniskoetter try { 9518ed59c51SJackson Toeniskoetter inserter.close(); 9528ed59c51SJackson Toeniskoetter } finally { 9538ed59c51SJackson Toeniskoetter db.close(); 9548ed59c51SJackson Toeniskoetter } 9558ed59c51SJackson Toeniskoetter } 9568ed59c51SJackson Toeniskoetter prunePacked(ObjectDirectory odb)957a57dd1c1SRobin Rosenberg private static void prunePacked(ObjectDirectory odb) throws IOException { 958efb154fcSNasser Grainawi for (Pack p : odb.getPacks()) { 959f5eb0d93SShawn O. Pearce for (MutableEntry e : p) 96045731756SMatthias Sohn FileUtils.delete(odb.fileFor(e.toObjectId())); 961f5eb0d93SShawn O. Pearce } 962f5eb0d93SShawn O. Pearce } 963f5eb0d93SShawn O. Pearce writeFile(File p, byte[] bin)9646d370d83SHan-Wen Nienhuys private void writeFile(File p, byte[] bin) throws IOException, 96589d4a737SShawn O. Pearce ObjectWritingException { 966e96cb22aSDavid Pursehouse final LockFile lck = new LockFile(p); 967f5eb0d93SShawn O. Pearce if (!lck.lock()) 968f5eb0d93SShawn O. Pearce throw new ObjectWritingException("Can't write " + p); 969f5eb0d93SShawn O. Pearce try { 970f5eb0d93SShawn O. Pearce lck.write(bin); 971f5eb0d93SShawn O. Pearce } catch (IOException ioe) { 9724cc13297SDavid Pursehouse throw new ObjectWritingException("Can't write " + p, ioe); 973f5eb0d93SShawn O. Pearce } 974f5eb0d93SShawn O. Pearce if (!lck.commit()) 975f5eb0d93SShawn O. Pearce throw new ObjectWritingException("Can't write " + p); 976f5eb0d93SShawn O. Pearce } 977f5eb0d93SShawn O. Pearce 978f945c424SShawn O. Pearce /** Helper to build a branch with one or more commits */ 979f945c424SShawn O. Pearce public class BranchBuilder { 980f945c424SShawn O. Pearce private final String ref; 981f945c424SShawn O. Pearce BranchBuilder(String ref)982f3ec7cf3SHan-Wen Nienhuys BranchBuilder(String ref) { 983f945c424SShawn O. Pearce this.ref = ref; 984f945c424SShawn O. Pearce } 985f945c424SShawn O. Pearce 986f945c424SShawn O. Pearce /** 987f945c424SShawn O. Pearce * @return construct a new commit builder that updates this branch. If 988f945c424SShawn O. Pearce * the branch already exists, the commit builder will have its 989f945c424SShawn O. Pearce * first parent as the current commit and its tree will be 990f945c424SShawn O. Pearce * initialized to the current files. 991f945c424SShawn O. Pearce * @throws Exception 992f945c424SShawn O. Pearce * the commit builder can't read the current branch state 993f945c424SShawn O. Pearce */ commit()994f945c424SShawn O. Pearce public CommitBuilder commit() throws Exception { 995f945c424SShawn O. Pearce return new CommitBuilder(this); 996f945c424SShawn O. Pearce } 997f945c424SShawn O. Pearce 998f945c424SShawn O. Pearce /** 999f945c424SShawn O. Pearce * Forcefully update this branch to a particular commit. 1000f945c424SShawn O. Pearce * 1001f945c424SShawn O. Pearce * @param to 1002f945c424SShawn O. Pearce * the commit to update to. 1003f945c424SShawn O. Pearce * @return {@code to}. 1004f945c424SShawn O. Pearce * @throws Exception 1005f945c424SShawn O. Pearce */ update(CommitBuilder to)1006f945c424SShawn O. Pearce public RevCommit update(CommitBuilder to) throws Exception { 1007f945c424SShawn O. Pearce return update(to.create()); 1008f945c424SShawn O. Pearce } 1009f945c424SShawn O. Pearce 1010f945c424SShawn O. Pearce /** 1011f945c424SShawn O. Pearce * Forcefully update this branch to a particular commit. 1012f945c424SShawn O. Pearce * 1013f945c424SShawn O. Pearce * @param to 1014f945c424SShawn O. Pearce * the commit to update to. 1015f945c424SShawn O. Pearce * @return {@code to}. 1016f945c424SShawn O. Pearce * @throws Exception 1017f945c424SShawn O. Pearce */ update(RevCommit to)1018f945c424SShawn O. Pearce public RevCommit update(RevCommit to) throws Exception { 1019f945c424SShawn O. Pearce return TestRepository.this.update(ref, to); 1020f945c424SShawn O. Pearce } 1021bc9df9c6SJonathan Nieder 1022bc9df9c6SJonathan Nieder /** 1023bc9df9c6SJonathan Nieder * Delete this branch. 1024bc9df9c6SJonathan Nieder * @throws Exception 1025bc9df9c6SJonathan Nieder * @since 4.4 1026bc9df9c6SJonathan Nieder */ delete()1027bc9df9c6SJonathan Nieder public void delete() throws Exception { 1028bc9df9c6SJonathan Nieder TestRepository.this.delete(ref); 1029bc9df9c6SJonathan Nieder } 1030f945c424SShawn O. Pearce } 1031f945c424SShawn O. Pearce 1032f945c424SShawn O. Pearce /** Helper to generate a commit. */ 1033f945c424SShawn O. Pearce public class CommitBuilder { 1034f945c424SShawn O. Pearce private final BranchBuilder branch; 1035f945c424SShawn O. Pearce 1036f945c424SShawn O. Pearce private final DirCache tree = DirCache.newInCore(); 1037f945c424SShawn O. Pearce 1038a5514932SSasa Zivkov private ObjectId topLevelTree; 1039a5514932SSasa Zivkov 10403b444863SDavid Pursehouse private final List<RevCommit> parents = new ArrayList<>(2); 1041f945c424SShawn O. Pearce 1042f945c424SShawn O. Pearce private int tick = 1; 1043f945c424SShawn O. Pearce 1044f945c424SShawn O. Pearce private String message = ""; 1045f945c424SShawn O. Pearce 1046f945c424SShawn O. Pearce private RevCommit self; 1047f945c424SShawn O. Pearce 10483d5e70bcSDave Borowitz private PersonIdent author; 10493d5e70bcSDave Borowitz private PersonIdent committer; 10503d5e70bcSDave Borowitz 10518b6f9aceSDave Borowitz private String changeId; 1052c1d40caaSDave Borowitz 1053da85ca73SDave Borowitz private boolean updateCommitterTime; 1054da85ca73SDave Borowitz CommitBuilder()1055f945c424SShawn O. Pearce CommitBuilder() { 1056f945c424SShawn O. Pearce branch = null; 1057f945c424SShawn O. Pearce } 1058f945c424SShawn O. Pearce CommitBuilder(BranchBuilder b)1059f945c424SShawn O. Pearce CommitBuilder(BranchBuilder b) throws Exception { 1060f945c424SShawn O. Pearce branch = b; 1061f945c424SShawn O. Pearce 10624c236ff4SMatthias Sohn Ref ref = db.exactRef(branch.ref); 10630b625445SDave Borowitz if (ref != null && ref.getObjectId() != null) 1064f945c424SShawn O. Pearce parent(pool.parseCommit(ref.getObjectId())); 1065f945c424SShawn O. Pearce } 1066f945c424SShawn O. Pearce CommitBuilder(CommitBuilder prior)1067f945c424SShawn O. Pearce CommitBuilder(CommitBuilder prior) throws Exception { 1068f945c424SShawn O. Pearce branch = prior.branch; 1069f945c424SShawn O. Pearce 1070f945c424SShawn O. Pearce DirCacheBuilder b = tree.builder(); 1071f945c424SShawn O. Pearce for (int i = 0; i < prior.tree.getEntryCount(); i++) 1072f945c424SShawn O. Pearce b.add(prior.tree.getEntry(i)); 1073f945c424SShawn O. Pearce b.finish(); 1074f945c424SShawn O. Pearce 1075f945c424SShawn O. Pearce parents.add(prior.create()); 1076f945c424SShawn O. Pearce } 1077f945c424SShawn O. Pearce 1078376c20f4SMatthias Sohn /** 1079376c20f4SMatthias Sohn * set parent commit 1080376c20f4SMatthias Sohn * 1081376c20f4SMatthias Sohn * @param p 1082376c20f4SMatthias Sohn * parent commit 1083376c20f4SMatthias Sohn * @return this commit builder 1084376c20f4SMatthias Sohn * @throws Exception 1085376c20f4SMatthias Sohn */ parent(RevCommit p)1086f945c424SShawn O. Pearce public CommitBuilder parent(RevCommit p) throws Exception { 1087f945c424SShawn O. Pearce if (parents.isEmpty()) { 1088f945c424SShawn O. Pearce DirCacheBuilder b = tree.builder(); 1089f945c424SShawn O. Pearce parseBody(p); 109094228bdeSShawn O. Pearce b.addTree(new byte[0], DirCacheEntry.STAGE_0, pool 109194228bdeSShawn O. Pearce .getObjectReader(), p.getTree()); 1092f945c424SShawn O. Pearce b.finish(); 1093f945c424SShawn O. Pearce } 1094f945c424SShawn O. Pearce parents.add(p); 1095f945c424SShawn O. Pearce return this; 1096f945c424SShawn O. Pearce } 1097f945c424SShawn O. Pearce 1098376c20f4SMatthias Sohn /** 1099376c20f4SMatthias Sohn * Get parent commits 1100376c20f4SMatthias Sohn * 1101376c20f4SMatthias Sohn * @return parent commits 1102376c20f4SMatthias Sohn */ parents()110382872182SDave Borowitz public List<RevCommit> parents() { 110482872182SDave Borowitz return Collections.unmodifiableList(parents); 110582872182SDave Borowitz } 110682872182SDave Borowitz 1107376c20f4SMatthias Sohn /** 1108376c20f4SMatthias Sohn * Remove parent commits 1109376c20f4SMatthias Sohn * 1110376c20f4SMatthias Sohn * @return this commit builder 1111376c20f4SMatthias Sohn */ noParents()1112f945c424SShawn O. Pearce public CommitBuilder noParents() { 1113f945c424SShawn O. Pearce parents.clear(); 1114f945c424SShawn O. Pearce return this; 1115f945c424SShawn O. Pearce } 1116f945c424SShawn O. Pearce 1117376c20f4SMatthias Sohn /** 1118376c20f4SMatthias Sohn * Remove files 1119376c20f4SMatthias Sohn * 1120376c20f4SMatthias Sohn * @return this commit builder 1121376c20f4SMatthias Sohn */ noFiles()1122f945c424SShawn O. Pearce public CommitBuilder noFiles() { 1123f945c424SShawn O. Pearce tree.clear(); 1124f945c424SShawn O. Pearce return this; 1125f945c424SShawn O. Pearce } 1126f945c424SShawn O. Pearce 1127376c20f4SMatthias Sohn /** 1128376c20f4SMatthias Sohn * Set top level tree 1129376c20f4SMatthias Sohn * 1130376c20f4SMatthias Sohn * @param treeId 1131376c20f4SMatthias Sohn * the top level tree 1132376c20f4SMatthias Sohn * @return this commit builder 1133376c20f4SMatthias Sohn */ setTopLevelTree(ObjectId treeId)1134a5514932SSasa Zivkov public CommitBuilder setTopLevelTree(ObjectId treeId) { 1135a5514932SSasa Zivkov topLevelTree = treeId; 1136a5514932SSasa Zivkov return this; 1137a5514932SSasa Zivkov } 1138a5514932SSasa Zivkov 1139376c20f4SMatthias Sohn /** 1140376c20f4SMatthias Sohn * Add file with given content 1141376c20f4SMatthias Sohn * 1142376c20f4SMatthias Sohn * @param path 1143376c20f4SMatthias Sohn * path of the file 1144376c20f4SMatthias Sohn * @param content 1145376c20f4SMatthias Sohn * the file content 1146376c20f4SMatthias Sohn * @return this commit builder 1147376c20f4SMatthias Sohn * @throws Exception 1148376c20f4SMatthias Sohn */ add(String path, String content)1149f945c424SShawn O. Pearce public CommitBuilder add(String path, String content) throws Exception { 1150f945c424SShawn O. Pearce return add(path, blob(content)); 1151f945c424SShawn O. Pearce } 1152f945c424SShawn O. Pearce 1153376c20f4SMatthias Sohn /** 1154376c20f4SMatthias Sohn * Add file with given path and blob 1155376c20f4SMatthias Sohn * 1156376c20f4SMatthias Sohn * @param path 1157376c20f4SMatthias Sohn * path of the file 1158376c20f4SMatthias Sohn * @param id 1159376c20f4SMatthias Sohn * blob for this file 1160376c20f4SMatthias Sohn * @return this commit builder 1161376c20f4SMatthias Sohn * @throws Exception 1162376c20f4SMatthias Sohn */ add(String path, RevBlob id)11636d370d83SHan-Wen Nienhuys public CommitBuilder add(String path, RevBlob id) 1164f945c424SShawn O. Pearce throws Exception { 11659346f1ccSDave Borowitz return edit(new PathEdit(path) { 1166f945c424SShawn O. Pearce @Override 1167f945c424SShawn O. Pearce public void apply(DirCacheEntry ent) { 1168f945c424SShawn O. Pearce ent.setFileMode(FileMode.REGULAR_FILE); 1169f945c424SShawn O. Pearce ent.setObjectId(id); 1170f945c424SShawn O. Pearce } 1171f945c424SShawn O. Pearce }); 11729346f1ccSDave Borowitz } 11739346f1ccSDave Borowitz 1174376c20f4SMatthias Sohn /** 1175376c20f4SMatthias Sohn * Edit the index 1176376c20f4SMatthias Sohn * 1177376c20f4SMatthias Sohn * @param edit 1178376c20f4SMatthias Sohn * the index record update 1179376c20f4SMatthias Sohn * @return this commit builder 1180376c20f4SMatthias Sohn */ edit(PathEdit edit)11819346f1ccSDave Borowitz public CommitBuilder edit(PathEdit edit) { 11829346f1ccSDave Borowitz DirCacheEditor e = tree.editor(); 11839346f1ccSDave Borowitz e.add(edit); 1184f945c424SShawn O. Pearce e.finish(); 1185f945c424SShawn O. Pearce return this; 1186f945c424SShawn O. Pearce } 1187f945c424SShawn O. Pearce 1188376c20f4SMatthias Sohn /** 1189376c20f4SMatthias Sohn * Remove a file 1190376c20f4SMatthias Sohn * 1191376c20f4SMatthias Sohn * @param path 1192376c20f4SMatthias Sohn * path of the file 1193376c20f4SMatthias Sohn * @return this commit builder 1194376c20f4SMatthias Sohn */ rm(String path)1195f945c424SShawn O. Pearce public CommitBuilder rm(String path) { 1196f945c424SShawn O. Pearce DirCacheEditor e = tree.editor(); 1197f945c424SShawn O. Pearce e.add(new DeletePath(path)); 1198f945c424SShawn O. Pearce e.add(new DeleteTree(path)); 1199f945c424SShawn O. Pearce e.finish(); 1200f945c424SShawn O. Pearce return this; 1201f945c424SShawn O. Pearce } 1202f945c424SShawn O. Pearce 1203376c20f4SMatthias Sohn /** 1204376c20f4SMatthias Sohn * Set commit message 1205376c20f4SMatthias Sohn * 1206376c20f4SMatthias Sohn * @param m 1207376c20f4SMatthias Sohn * the message 1208376c20f4SMatthias Sohn * @return this commit builder 1209376c20f4SMatthias Sohn */ message(String m)1210f945c424SShawn O. Pearce public CommitBuilder message(String m) { 1211f945c424SShawn O. Pearce message = m; 1212f945c424SShawn O. Pearce return this; 1213f945c424SShawn O. Pearce } 1214f945c424SShawn O. Pearce 1215376c20f4SMatthias Sohn /** 1216376c20f4SMatthias Sohn * Get the commit message 1217376c20f4SMatthias Sohn * 1218376c20f4SMatthias Sohn * @return the commit message 1219376c20f4SMatthias Sohn */ message()122082872182SDave Borowitz public String message() { 122182872182SDave Borowitz return message; 122282872182SDave Borowitz } 122382872182SDave Borowitz 1224376c20f4SMatthias Sohn /** 1225376c20f4SMatthias Sohn * Tick the clock 1226376c20f4SMatthias Sohn * 1227376c20f4SMatthias Sohn * @param secs 1228376c20f4SMatthias Sohn * number of seconds 1229376c20f4SMatthias Sohn * @return this commit builder 1230376c20f4SMatthias Sohn */ tick(int secs)1231f945c424SShawn O. Pearce public CommitBuilder tick(int secs) { 1232f945c424SShawn O. Pearce tick = secs; 1233f945c424SShawn O. Pearce return this; 1234f945c424SShawn O. Pearce } 1235f945c424SShawn O. Pearce 1236376c20f4SMatthias Sohn /** 1237376c20f4SMatthias Sohn * Set author and committer identity 1238376c20f4SMatthias Sohn * 1239376c20f4SMatthias Sohn * @param ident 1240376c20f4SMatthias Sohn * identity to set 1241376c20f4SMatthias Sohn * @return this commit builder 1242376c20f4SMatthias Sohn */ ident(PersonIdent ident)12433d5e70bcSDave Borowitz public CommitBuilder ident(PersonIdent ident) { 12443d5e70bcSDave Borowitz author = ident; 12453d5e70bcSDave Borowitz committer = ident; 12463d5e70bcSDave Borowitz return this; 12473d5e70bcSDave Borowitz } 12483d5e70bcSDave Borowitz 1249376c20f4SMatthias Sohn /** 1250376c20f4SMatthias Sohn * Set the author identity 1251376c20f4SMatthias Sohn * 1252376c20f4SMatthias Sohn * @param a 1253376c20f4SMatthias Sohn * the author's identity 1254376c20f4SMatthias Sohn * @return this commit builder 1255376c20f4SMatthias Sohn */ author(PersonIdent a)12563d5e70bcSDave Borowitz public CommitBuilder author(PersonIdent a) { 12573d5e70bcSDave Borowitz author = a; 12583d5e70bcSDave Borowitz return this; 12593d5e70bcSDave Borowitz } 12603d5e70bcSDave Borowitz 1261376c20f4SMatthias Sohn /** 1262376c20f4SMatthias Sohn * Get the author identity 1263376c20f4SMatthias Sohn * 1264376c20f4SMatthias Sohn * @return the author identity 1265376c20f4SMatthias Sohn */ author()126682872182SDave Borowitz public PersonIdent author() { 126782872182SDave Borowitz return author; 126882872182SDave Borowitz } 126982872182SDave Borowitz 1270376c20f4SMatthias Sohn /** 1271376c20f4SMatthias Sohn * Set the committer identity 1272376c20f4SMatthias Sohn * 1273376c20f4SMatthias Sohn * @param c 1274376c20f4SMatthias Sohn * the committer identity 1275376c20f4SMatthias Sohn * @return this commit builder 1276376c20f4SMatthias Sohn */ committer(PersonIdent c)12773d5e70bcSDave Borowitz public CommitBuilder committer(PersonIdent c) { 12783d5e70bcSDave Borowitz committer = c; 12793d5e70bcSDave Borowitz return this; 12803d5e70bcSDave Borowitz } 12813d5e70bcSDave Borowitz 1282376c20f4SMatthias Sohn /** 1283376c20f4SMatthias Sohn * Get the committer identity 1284376c20f4SMatthias Sohn * 1285376c20f4SMatthias Sohn * @return the committer identity 1286376c20f4SMatthias Sohn */ committer()128782872182SDave Borowitz public PersonIdent committer() { 128882872182SDave Borowitz return committer; 128982872182SDave Borowitz } 129082872182SDave Borowitz 1291376c20f4SMatthias Sohn /** 1292376c20f4SMatthias Sohn * Insert changeId 1293376c20f4SMatthias Sohn * 1294376c20f4SMatthias Sohn * @return this commit builder 1295376c20f4SMatthias Sohn */ insertChangeId()1296c1d40caaSDave Borowitz public CommitBuilder insertChangeId() { 12978b6f9aceSDave Borowitz changeId = ""; 12988b6f9aceSDave Borowitz return this; 12998b6f9aceSDave Borowitz } 13008b6f9aceSDave Borowitz 1301376c20f4SMatthias Sohn /** 1302376c20f4SMatthias Sohn * Insert given changeId 1303376c20f4SMatthias Sohn * 1304376c20f4SMatthias Sohn * @param c 1305376c20f4SMatthias Sohn * changeId 1306376c20f4SMatthias Sohn * @return this commit builder 1307376c20f4SMatthias Sohn */ insertChangeId(String c)13088b6f9aceSDave Borowitz public CommitBuilder insertChangeId(String c) { 13098b6f9aceSDave Borowitz // Validate, but store as a string so we can use "" as a sentinel. 13108b6f9aceSDave Borowitz ObjectId.fromString(c); 13118b6f9aceSDave Borowitz changeId = c; 1312c1d40caaSDave Borowitz return this; 1313c1d40caaSDave Borowitz } 1314c1d40caaSDave Borowitz 1315376c20f4SMatthias Sohn /** 1316376c20f4SMatthias Sohn * Create the commit 1317376c20f4SMatthias Sohn * 1318376c20f4SMatthias Sohn * @return the new commit 1319376c20f4SMatthias Sohn * @throws Exception 1320376c20f4SMatthias Sohn * if creation failed 1321376c20f4SMatthias Sohn */ create()1322f945c424SShawn O. Pearce public RevCommit create() throws Exception { 1323f945c424SShawn O. Pearce if (self == null) { 1324f945c424SShawn O. Pearce TestRepository.this.tick(tick); 1325f945c424SShawn O. Pearce 132622b28569SShawn O. Pearce final org.eclipse.jgit.lib.CommitBuilder c; 132722b28569SShawn O. Pearce 132822b28569SShawn O. Pearce c = new org.eclipse.jgit.lib.CommitBuilder(); 1329b46b635cSShawn O. Pearce c.setParentIds(parents); 13303e2b9b69SShawn O. Pearce setAuthorAndCommitter(c); 13313d5e70bcSDave Borowitz if (author != null) 13323d5e70bcSDave Borowitz c.setAuthor(author); 1333da85ca73SDave Borowitz if (committer != null) { 1334da85ca73SDave Borowitz if (updateCommitterTime) 1335069040ddSTerry Parker committer = new PersonIdent(committer, getDate()); 13363d5e70bcSDave Borowitz c.setCommitter(committer); 1337da85ca73SDave Borowitz } 1338f945c424SShawn O. Pearce 133988530a17SShawn O. Pearce ObjectId commitId; 13406599111dSDave Borowitz try (ObjectInserter ins = inserter) { 1341a5514932SSasa Zivkov if (topLevelTree != null) 1342a5514932SSasa Zivkov c.setTreeId(topLevelTree); 1343a5514932SSasa Zivkov else 13446599111dSDave Borowitz c.setTreeId(tree.writeTree(ins)); 1345c1d40caaSDave Borowitz insertChangeId(c); 1346c1d40caaSDave Borowitz c.setMessage(message); 13476599111dSDave Borowitz commitId = ins.insert(c); 13486599111dSDave Borowitz ins.flush(); 134988530a17SShawn O. Pearce } 135000e51e35STerry Parker self = pool.parseCommit(commitId); 1351f945c424SShawn O. Pearce 1352f945c424SShawn O. Pearce if (branch != null) 1353f945c424SShawn O. Pearce branch.update(self); 1354f945c424SShawn O. Pearce } 1355f945c424SShawn O. Pearce return self; 1356f945c424SShawn O. Pearce } 1357f945c424SShawn O. Pearce insertChangeId(org.eclipse.jgit.lib.CommitBuilder c)13583af05f60SMatthias Sohn private void insertChangeId(org.eclipse.jgit.lib.CommitBuilder c) { 13598b6f9aceSDave Borowitz if (changeId == null) 13608b6f9aceSDave Borowitz return; 13616ed07f5cSDave Borowitz int idx = ChangeIdUtil.indexOfChangeId(message, "\n"); 13626ed07f5cSDave Borowitz if (idx >= 0) 13636ed07f5cSDave Borowitz return; 13646ed07f5cSDave Borowitz 1365c1d40caaSDave Borowitz ObjectId firstParentId = null; 1366c1d40caaSDave Borowitz if (!parents.isEmpty()) 1367c1d40caaSDave Borowitz firstParentId = parents.get(0); 13688b6f9aceSDave Borowitz 13698b6f9aceSDave Borowitz ObjectId cid; 13700db509f7SCarsten Hammer if (changeId.isEmpty()) 13718b6f9aceSDave Borowitz cid = ChangeIdUtil.computeChangeId(c.getTreeId(), firstParentId, 13728b6f9aceSDave Borowitz c.getAuthor(), c.getCommitter(), message); 13738b6f9aceSDave Borowitz else 13748b6f9aceSDave Borowitz cid = ObjectId.fromString(changeId); 13758b6f9aceSDave Borowitz message = ChangeIdUtil.insertId(message, cid); 13768b6f9aceSDave Borowitz if (cid != null) 1377c1d40caaSDave Borowitz message = message.replaceAll("\nChange-Id: I" //$NON-NLS-1$ 1378c1d40caaSDave Borowitz + ObjectId.zeroId().getName() + "\n", "\nChange-Id: I" //$NON-NLS-1$ //$NON-NLS-2$ 13798b6f9aceSDave Borowitz + cid.getName() + "\n"); //$NON-NLS-1$ 1380c1d40caaSDave Borowitz } 1381c1d40caaSDave Borowitz 1382376c20f4SMatthias Sohn /** 1383376c20f4SMatthias Sohn * Create child commit builder 1384376c20f4SMatthias Sohn * 1385376c20f4SMatthias Sohn * @return child commit builder 1386376c20f4SMatthias Sohn * @throws Exception 1387376c20f4SMatthias Sohn */ child()1388f945c424SShawn O. Pearce public CommitBuilder child() throws Exception { 1389f945c424SShawn O. Pearce return new CommitBuilder(this); 1390f945c424SShawn O. Pearce } 1391f945c424SShawn O. Pearce } 1392f945c424SShawn O. Pearce } 1393