/* * Copyright (C) 2024, Broadcom and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at * https://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.api; import static org.eclipse.jgit.lib.Constants.HEAD; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Iterator; import org.eclipse.jgit.internal.storage.file.FileRepository; import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ReflogEntry; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS.ExecutionResult; import org.eclipse.jgit.util.RawParseUtils; import org.eclipse.jgit.util.TemporaryBuffer; import org.junit.Test; public class LinkedWorktreeTest extends RepositoryTestCase { @Override public void setUp() throws Exception { super.setUp(); try (Git git = new Git(db)) { git.commit().setMessage("Initial commit").call(); } } @Test public void testWeCanReadFromLinkedWorktreeFromBare() throws Exception { FS fs = db.getFS(); File directory = trash.getParentFile(); String dbDirName = db.getWorkTree().getName(); cloneBare(fs, directory, dbDirName, "bare"); File bareDirectory = new File(directory, "bare"); worktreeAddExisting(fs, bareDirectory, "master"); File worktreesDir = new File(bareDirectory, "worktrees"); File masterWorktreesDir = new File(worktreesDir, "master"); FileRepository repository = new FileRepository(masterWorktreesDir); try (Git git = new Git(repository)) { ObjectId objectId = repository.resolve(HEAD); assertNotNull(objectId); Iterator log = git.log().all().call().iterator(); assertTrue(log.hasNext()); assertTrue("Initial commit".equals(log.next().getShortMessage())); // we have reflog entry // depending on git version we either have one or // two entries where extra is zeroid entry with // same message or no message Collection reflog = git.reflog().call(); assertNotNull(reflog); assertTrue(reflog.size() > 0); ReflogEntry[] reflogs = reflog.toArray(new ReflogEntry[0]); assertEquals(reflogs[reflogs.length - 1].getComment(), "reset: moving to HEAD"); // index works with file changes File masterDir = new File(directory, "master"); File testFile = new File(masterDir, "test"); Status status = git.status().call(); assertTrue(status.getUncommittedChanges().size() == 0); assertTrue(status.getUntracked().size() == 0); JGitTestUtil.write(testFile, "test"); status = git.status().call(); assertTrue(status.getUncommittedChanges().size() == 0); assertTrue(status.getUntracked().size() == 1); git.add().addFilepattern("test").call(); status = git.status().call(); assertTrue(status.getUncommittedChanges().size() == 1); assertTrue(status.getUntracked().size() == 0); } } @Test public void testWeCanReadFromLinkedWorktreeFromNonBare() throws Exception { FS fs = db.getFS(); worktreeAddNew(fs, db.getWorkTree(), "wt"); File worktreesDir = new File(db.getDirectory(), "worktrees"); File masterWorktreesDir = new File(worktreesDir, "wt"); FileRepository repository = new FileRepository(masterWorktreesDir); try (Git git = new Git(repository)) { ObjectId objectId = repository.resolve(HEAD); assertNotNull(objectId); Iterator log = git.log().all().call().iterator(); assertTrue(log.hasNext()); assertTrue("Initial commit".equals(log.next().getShortMessage())); // we have reflog entry Collection reflog = git.reflog().call(); assertNotNull(reflog); assertTrue(reflog.size() > 0); ReflogEntry[] reflogs = reflog.toArray(new ReflogEntry[0]); assertEquals(reflogs[reflogs.length - 1].getComment(), "reset: moving to HEAD"); // index works with file changes File directory = trash.getParentFile(); File wtDir = new File(directory, "wt"); File testFile = new File(wtDir, "test"); Status status = git.status().call(); assertTrue(status.getUncommittedChanges().size() == 0); assertTrue(status.getUntracked().size() == 0); JGitTestUtil.write(testFile, "test"); status = git.status().call(); assertTrue(status.getUncommittedChanges().size() == 0); assertTrue(status.getUntracked().size() == 1); git.add().addFilepattern("test").call(); status = git.status().call(); assertTrue(status.getUncommittedChanges().size() == 1); assertTrue(status.getUntracked().size() == 0); } } private static void cloneBare(FS fs, File directory, String from, String to) throws IOException, InterruptedException { ProcessBuilder builder = fs.runInShell("git", new String[] { "clone", "--bare", from, to }); builder.directory(directory); builder.environment().put("HOME", fs.userHome().getAbsolutePath()); StringBuilder input = new StringBuilder(); ExecutionResult result = fs.execute(builder, new ByteArrayInputStream( input.toString().getBytes(StandardCharsets.UTF_8))); String stdOut = toString(result.getStdout()); String errorOut = toString(result.getStderr()); assertNotNull(stdOut); assertNotNull(errorOut); } private static void worktreeAddExisting(FS fs, File directory, String name) throws IOException, InterruptedException { ProcessBuilder builder = fs.runInShell("git", new String[] { "worktree", "add", "../" + name, name }); builder.directory(directory); builder.environment().put("HOME", fs.userHome().getAbsolutePath()); StringBuilder input = new StringBuilder(); ExecutionResult result = fs.execute(builder, new ByteArrayInputStream( input.toString().getBytes(StandardCharsets.UTF_8))); String stdOut = toString(result.getStdout()); String errorOut = toString(result.getStderr()); assertNotNull(stdOut); assertNotNull(errorOut); } private static void worktreeAddNew(FS fs, File directory, String name) throws IOException, InterruptedException { ProcessBuilder builder = fs.runInShell("git", new String[] { "worktree", "add", "-b", name, "../" + name, "master"}); builder.directory(directory); builder.environment().put("HOME", fs.userHome().getAbsolutePath()); StringBuilder input = new StringBuilder(); ExecutionResult result = fs.execute(builder, new ByteArrayInputStream( input.toString().getBytes(StandardCharsets.UTF_8))); String stdOut = toString(result.getStdout()); String errorOut = toString(result.getStderr()); assertNotNull(stdOut); assertNotNull(errorOut); } private static String toString(TemporaryBuffer b) throws IOException { return RawParseUtils.decode(b.toByteArray()); } }