diff options
37 files changed, 989 insertions, 164 deletions
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target new file mode 100644 index 0000000000..fac43dc922 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target @@ -0,0 +1,290 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?pde?> +<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl --> +<target name="jgit-4.33" sequenceNumber="1721077009"> + <locations> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/> + <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/> + <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/> + <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/> + <unit id="net.i2p.crypto.eddsa" version="0.3.0"/> + <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/> + <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/> + <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/> + <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/> + <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/> + <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/> + <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/> + <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/> + <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/> + <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/> + <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/> + <unit id="org.junit" version="4.13.2.v20230809-1000"/> + <unit id="org.junit.source" version="4.13.2.v20230809-1000"/> + <unit id="org.objenesis" version="3.4.0"/> + <unit id="org.objenesis.source" version="3.4.0"/> + <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/> + <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/> + <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-09"/> + </location> + <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit"> + <unit id="org.eclipse.osgi" version="0.0.0"/> + <repository location="https://download.eclipse.org/staging/2024-09/"/> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz"> + <dependencies> + <dependency> + <groupId>org.tukaani</groupId> + <artifactId>xz</artifactId> + <version>1.9</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j"> + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.7.36</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>1.7.36</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd"> + <dependencies> + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-osgi</artifactId> + <version>2.13.1</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-sftp</artifactId> + <version>2.13.1</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito"> + <dependencies> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>5.12.0</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna"> + <dependencies> + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna</artifactId> + <version>5.14.0</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna-platform</artifactId> + <version>5.14.0</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty"> + <dependencies> + <dependency> + <groupId>org.eclipse.jetty.ee10</groupId> + <artifactId>jetty-ee10-servlet</artifactId> + <version>12.0.10</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-http</artifactId> + <version>12.0.10</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-io</artifactId> + <version>12.0.10</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-security</artifactId> + <version>12.0.10</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>12.0.10</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-session</artifactId> + <version>12.0.10</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-util</artifactId> + <version>12.0.10</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-util-ajax</artifactId> + <version>12.0.10</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>jakarta.servlet</groupId> + <artifactId>jakarta.servlet-api</artifactId> + <version>6.0.0</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah"> + <dependencies> + <dependency> + <groupId>com.googlecode.javaewah</groupId> + <artifactId>JavaEWAH</artifactId> + <version>1.2.3</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest"> + <dependencies> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <version>2.2</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson"> + <dependencies> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.11.0</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy"> + <dependencies> + <dependency> + <groupId>net.bytebuddy</groupId> + <artifactId>byte-buddy</artifactId> + <version>1.14.17</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>net.bytebuddy</groupId> + <artifactId>byte-buddy-agent</artifactId> + <version>1.14.17</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle"> + <dependencies> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcpg-jdk18on</artifactId> + <version>1.78.1</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcprov-jdk18on</artifactId> + <version>1.78.1</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcpkix-jdk18on</artifactId> + <version>1.78.1</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcutil-jdk18on</artifactId> + <version>1.78.1</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj"> + <dependencies> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>3.26.0</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j"> + <dependencies> + <dependency> + <groupId>args4j</groupId> + <artifactId>args4j</artifactId> + <version>2.37</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache"> + <dependencies> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + <version>1.17.0</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-compress</artifactId> + <version>1.26.2</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.14.0</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.16.1</version> + <type>jar</type> + </dependency> + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>1.3.2</version> + <type>jar</type> + </dependency> + </dependencies> + </location> + </locations> +</target> diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd new file mode 100644 index 0000000000..d01a8a98ba --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd @@ -0,0 +1,8 @@ +target "jgit-4.33" with source configurePhase + +include "orbit/orbit-4.33.tpd" +include "maven/dependencies.tpd" + +location "https://download.eclipse.org/staging/2024-09/" { + org.eclipse.osgi lazy +} diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd index 0554a8578c..9d00cb4c4f 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd @@ -1,4 +1,4 @@ -target "orbit-4.30" with source configurePhase +target "orbit-4.31" with source configurePhase // see https://download.eclipse.org/tools/orbit/downloads/ location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12" { diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd new file mode 100644 index 0000000000..8dca4cb681 --- /dev/null +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd @@ -0,0 +1,27 @@ +target "orbit-4.33" with source configurePhase +// see https://download.eclipse.org/tools/orbit/downloads/ + +location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-09" { + com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400] + com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400] + com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400] + com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400] + net.i2p.crypto.eddsa [0.3.0,0.3.0] + net.i2p.crypto.eddsa.source [0.3.0,0.3.0] + org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200] + org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200] + org.apache.httpcomponents.httpclient [4.5.14,4.5.14] + org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14] + org.apache.httpcomponents.httpcore [4.4.16,4.4.16] + org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16] + org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000] + org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000] + org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000] + org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000] + org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000] + org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000] + org.objenesis [3.4,3.4] + org.objenesis.source [3.4,3.4] + org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733] + org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733] +} diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java index 52f40c2957..f5de7045d0 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java @@ -94,7 +94,7 @@ class Config extends TextBuiltin { if (global || isListAll()) list(SystemReader.getInstance().openUserConfig(null, fs)); if (local || isListAll()) - list(new FileBasedConfig(fs.resolve(getRepository().getDirectory(), + list(new FileBasedConfig(fs.resolve(getRepository().getCommonDirectory(), Constants.CONFIG), fs)); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java index b937b1f6a9..4c971ffb6b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java @@ -559,7 +559,7 @@ public class EolRepositoryTest extends RepositoryTestCase { } if (infoAttributesContent != null) { - File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES); + File f = new File(db.getCommonDirectory(), Constants.INFO_ATTRIBUTES); write(f, infoAttributesContent); } config.save(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java new file mode 100644 index 0000000000..3b60e1b5c0 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java @@ -0,0 +1,192 @@ +/* + * 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<RevCommit> 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<ReflogEntry> 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<RevCommit> log = git.log().all().call().iterator(); + assertTrue(log.hasNext()); + assertTrue("Initial commit".equals(log.next().getShortMessage())); + + // we have reflog entry + Collection<ReflogEntry> 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()); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java index 7fb98ec53b..c41dd81add 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java @@ -584,7 +584,7 @@ public class AttributesHandlerTest extends RepositoryTestCase { } if (infoAttributesContent != null) { - File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES); + File f = new File(db.getCommonDirectory(), Constants.INFO_ATTRIBUTES); write(f, infoAttributesContent); } config.save(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java index daf4382719..1af42cb229 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java @@ -171,7 +171,7 @@ public class BatchRefUpdateTest extends LocalDiskRepositoryTestCase { assertEquals(c2.getResult(), ReceiveCommand.Result.OK); } - File packed = new File(diskRepo.getDirectory(), "packed-refs"); + File packed = new File(diskRepo.getCommonDirectory(), "packed-refs"); String packedStr = new String(Files.readAllBytes(packed.toPath()), UTF_8); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java index 8baa3cc341..c57295518d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java @@ -58,7 +58,7 @@ public class GcPackRefsTest extends GcTestCase { String ref = "dir/ref"; tr.branch(ref).commit().create(); String name = repo.findRef(ref).getName(); - Path dir = repo.getDirectory().toPath().resolve(name).getParent(); + Path dir = repo.getCommonDirectory().toPath().resolve(name).getParent(); assertNotNull(dir); gc.packRefs(); assertFalse(Files.exists(dir)); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java index e6c1ee5fd6..29f180d76b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java @@ -30,7 +30,7 @@ public class GcReflogTest extends GcTestCase { BranchBuilder bb = tr.branch("refs/heads/master"); bb.commit().add("A", "A").add("B", "B").create(); bb.commit().add("A", "A2").add("B", "B2").create(); - new File(repo.getDirectory(), Constants.LOGS + "/refs/heads/master") + new File(repo.getCommonDirectory(), Constants.LOGS + "/refs/heads/master") .delete(); stats = gc.getStatistics(); assertEquals(8, stats.numberOfLooseObjects); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java index 2bafde65d3..baa0182b87 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java @@ -90,25 +90,26 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase { @Test public void testCreate() throws IOException { // setUp above created the directory. We just have to test it. - File d = diskRepo.getDirectory(); + File gitDir = diskRepo.getDirectory(); + File commonDir = diskRepo.getCommonDirectory(); assertSame(diskRepo, refdir.getRepository()); - assertTrue(new File(d, "refs").isDirectory()); - assertTrue(new File(d, "logs").isDirectory()); - assertTrue(new File(d, "logs/refs").isDirectory()); - assertFalse(new File(d, "packed-refs").exists()); + assertTrue(new File(commonDir, "refs").isDirectory()); + assertTrue(new File(commonDir, "logs").isDirectory()); + assertTrue(new File(commonDir, "logs/refs").isDirectory()); + assertFalse(new File(commonDir, "packed-refs").exists()); - assertTrue(new File(d, "refs/heads").isDirectory()); - assertTrue(new File(d, "refs/tags").isDirectory()); - assertEquals(2, new File(d, "refs").list().length); - assertEquals(0, new File(d, "refs/heads").list().length); - assertEquals(0, new File(d, "refs/tags").list().length); + assertTrue(new File(commonDir, "refs/heads").isDirectory()); + assertTrue(new File(commonDir, "refs/tags").isDirectory()); + assertEquals(2, new File(commonDir, "refs").list().length); + assertEquals(0, new File(commonDir, "refs/heads").list().length); + assertEquals(0, new File(commonDir, "refs/tags").list().length); - assertTrue(new File(d, "logs/refs/heads").isDirectory()); - assertFalse(new File(d, "logs/HEAD").exists()); - assertEquals(0, new File(d, "logs/refs/heads").list().length); + assertTrue(new File(commonDir, "logs/refs/heads").isDirectory()); + assertFalse(new File(gitDir, "logs/HEAD").exists()); + assertEquals(0, new File(commonDir, "logs/refs/heads").list().length); - assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD))); + assertEquals("ref: refs/heads/master\n", read(new File(gitDir, HEAD))); } @Test(expected = UnsupportedOperationException.class) @@ -1382,7 +1383,7 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase { } private void deleteLooseRef(String name) { - File path = new File(diskRepo.getDirectory(), name); + File path = new File(diskRepo.getCommonDirectory(), name); assertTrue("deleted " + name, path.delete()); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java index dc0e749373..eb521ff9eb 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java @@ -238,7 +238,7 @@ public class ReflogReaderTest extends SampleDataRepositoryTestCase { private void setupReflog(String logName, byte[] data) throws FileNotFoundException, IOException { - File logfile = new File(db.getDirectory(), logName); + File logfile = new File(db.getCommonDirectory(), logName); if (!logfile.getParentFile().mkdirs() && !logfile.getParentFile().isDirectory()) { throw new IOException( diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java index 8d0e99dea0..8e9b7b84bd 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java @@ -48,7 +48,7 @@ public class ReflogWriterTest extends SampleDataRepositoryTestCase { private void readReflog(byte[] buffer) throws FileNotFoundException, IOException { - File logfile = new File(db.getDirectory(), "logs/refs/heads/master"); + File logfile = new File(db.getCommonDirectory(), "logs/refs/heads/master"); if (!logfile.getParentFile().mkdirs() && !logfile.getParentFile().isDirectory()) { throw new IOException( diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index a15fe1f372..c9f7336609 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -808,6 +808,7 @@ truncatedHunkOldLinesMissing=Truncated hunk, at least {0} old lines is missing tSizeMustBeGreaterOrEqual1=tSize must be >= 1 unableToCheckConnectivity=Unable to check connectivity. unableToCreateNewObject=Unable to create new object: {0} +unableToReadFullArray=Unable to read an array with {0} elements from the stream unableToReadFullInt=Unable to read a full int from the stream unableToReadPackfile=Unable to read packfile {0} unableToRemovePath=Unable to remove path ''{0}'' diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java index 8fb5d60b85..401f069e4e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java @@ -176,7 +176,7 @@ public class SubmoduleAddCommand extends CloneCommand clone = Git.cloneRepository(); configure(clone); clone.setDirectory(moduleDirectory); - clone.setGitDir(new File(new File(repo.getDirectory(), + clone.setGitDir(new File(new File(repo.getCommonDirectory(), Constants.MODULES), path)); clone.setURI(resolvedUri); if (monitor != null) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java index df73164161..751dabcd6b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java @@ -128,7 +128,7 @@ public class SubmoduleUpdateCommand extends clone.setURI(url); clone.setDirectory(generator.getDirectory()); clone.setGitDir( - new File(new File(repo.getDirectory(), Constants.MODULES), + new File(new File(repo.getCommonDirectory(), Constants.MODULES), generator.getPath())); if (monitor != null) { clone.setProgressMonitor(monitor); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index 6ae5153c12..fa0a82fd58 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -1647,6 +1647,8 @@ public class DirCacheCheckout { filterProcessBuilder.directory(repo.getWorkTree()); filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY, repo.getDirectory().getAbsolutePath()); + filterProcessBuilder.environment().put(Constants.GIT_COMMON_DIR_KEY, + repo.getCommonDirectory().getAbsolutePath()); ExecutionResult result; int rc; try { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index e31533aaa2..8a5f2b2b30 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -838,6 +838,7 @@ public class JGitText extends TranslationBundle { /***/ public String unableToCheckConnectivity; /***/ public String unableToCreateNewObject; /***/ public String unableToReadFullInt; + /***/ public String unableToReadFullArray; /***/ public String unableToReadPackfile; /***/ public String unableToRemovePath; /***/ public String unableToWrite; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java index b94a84a41c..cdd10618e0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java @@ -330,18 +330,27 @@ public final class DfsPackFile extends BlockBasedFile { PackIndex idx = idx(ctx); DfsStreamKey revKey = desc.getStreamKey(REVERSE_INDEX); - AtomicBoolean cacheHit = new AtomicBoolean(true); - DfsBlockCache.Ref<PackReverseIndex> revref = cache.getOrLoadRef(revKey, - REF_POSITION, () -> { - cacheHit.set(false); - return loadReverseIdx(ctx, revKey, idx); + // Keep the value parsed in the loader, in case the Ref<> is + // nullified in ClockBlockCacheTable#reserveSpace + // before we read its value. + AtomicReference<PackReverseIndex> loadedRef = new AtomicReference<>( + null); + DfsBlockCache.Ref<PackReverseIndex> cachedRef = cache + .getOrLoadRef(revKey, REF_POSITION, () -> { + RefWithSize<PackReverseIndex> ridx = loadReverseIdx(ctx, + idx); + loadedRef.set(ridx.ref); + return new DfsBlockCache.Ref<>(revKey, REF_POSITION, + ridx.size, ridx.ref); }); - if (cacheHit.get()) { + if (loadedRef.get() == null) { ctx.stats.ridxCacheHit++; } - PackReverseIndex revidx = revref.get(); - if (reverseIndex == null && revidx != null) { - reverseIndex = revidx; + reverseIndex = cachedRef.get() != null ? cachedRef.get() + : loadedRef.get(); + if (reverseIndex == null) { + throw new IOException( + "Couldn't get a reference to the reverse index"); //$NON-NLS-1$ } ctx.emitIndexLoad(desc, REVERSE_INDEX, reverseIndex); return reverseIndex; @@ -1241,18 +1250,13 @@ public final class DfsPackFile extends BlockBasedFile { } } - private DfsBlockCache.Ref<PackReverseIndex> loadReverseIdx( - DfsReader ctx, DfsStreamKey revKey, PackIndex idx) { + private static RefWithSize<PackReverseIndex> loadReverseIdx(DfsReader ctx, + PackIndex idx) { ctx.stats.readReverseIdx++; long start = System.nanoTime(); PackReverseIndex revidx = PackReverseIndexFactory.computeFromIndex(idx); - reverseIndex = revidx; ctx.stats.readReverseIdxMicros += elapsedMicros(start); - return new DfsBlockCache.Ref<>( - revKey, - REF_POSITION, - idx.getObjectCount() * 8, - revidx); + return new RefWithSize<>(revidx, idx.getObjectCount() * 8); } private DfsBlockCache.Ref<PackObjectSizeIndex> loadObjectSizeIndex( diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java index ed2516ddd0..80240e5062 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java @@ -68,14 +68,14 @@ public class FileReftableDatabase extends RefDatabase { private final FileReftableStack reftableStack; FileReftableDatabase(FileRepository repo) throws IOException { - this(repo, new File(new File(repo.getDirectory(), Constants.REFTABLE), + this(repo, new File(new File(repo.getCommonDirectory(), Constants.REFTABLE), Constants.TABLES_LIST)); } FileReftableDatabase(FileRepository repo, File refstackName) throws IOException { this.fileRepository = repo; this.reftableStack = new FileReftableStack(refstackName, - new File(fileRepository.getDirectory(), Constants.REFTABLE), + new File(fileRepository.getCommonDirectory(), Constants.REFTABLE), () -> fileRepository.fireEvent(new RefsChangedEvent()), () -> fileRepository.getConfig()); this.reftableDatabase = new ReftableDatabase() { @@ -318,7 +318,7 @@ public class FileReftableDatabase extends RefDatabase { @Override public void create() throws IOException { FileUtils.mkdir( - new File(fileRepository.getDirectory(), Constants.REFTABLE), + new File(fileRepository.getCommonDirectory(), Constants.REFTABLE), true); } @@ -615,7 +615,7 @@ public class FileReftableDatabase extends RefDatabase { FileReftableDatabase newDb = null; File reftableList = null; try { - File reftableDir = new File(repo.getDirectory(), + File reftableDir = new File(repo.getCommonDirectory(), Constants.REFTABLE); reftableList = new File(reftableDir, Constants.TABLES_LIST); if (!reftableDir.isDirectory()) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java index e5a00d3925..b5d29a3fc8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java @@ -165,7 +165,7 @@ public class FileRepository extends Repository { throw new IOException(e.getMessage(), e); } repoConfig = new FileBasedConfig(userConfig, getFS().resolve( - getDirectory(), Constants.CONFIG), + getCommonDirectory(), Constants.CONFIG), getFS()); loadRepoConfig(); @@ -193,7 +193,7 @@ public class FileRepository extends Repository { options.getObjectDirectory(), // options.getAlternateObjectDirectories(), // getFS(), // - new File(getDirectory(), Constants.SHALLOW)); + new File(getCommonDirectory(), Constants.SHALLOW)); if (objectDatabase.exists()) { if (repositoryFormatVersion > 1) @@ -622,16 +622,17 @@ public class FileRepository extends Repository { * on IO problem */ void convertToPackedRefs(boolean writeLogs, boolean backup) throws IOException { + File commonDirectory = getCommonDirectory(); List<Ref> all = refs.getRefs(); - File packedRefs = new File(getDirectory(), Constants.PACKED_REFS); + File packedRefs = new File(commonDirectory, Constants.PACKED_REFS); if (packedRefs.exists()) { throw new IOException(MessageFormat.format(JGitText.get().fileAlreadyExists, packedRefs.getName())); } - File refsFile = new File(getDirectory(), "refs"); //$NON-NLS-1$ + File refsFile = new File(commonDirectory, "refs"); //$NON-NLS-1$ File refsHeadsFile = new File(refsFile, "heads");//$NON-NLS-1$ - File headFile = new File(getDirectory(), Constants.HEAD); + File headFile = new File(commonDirectory, Constants.HEAD); FileReftableDatabase oldDb = (FileReftableDatabase) refs; // Remove the dummy files that ensure compatibility with older git @@ -701,7 +702,7 @@ public class FileRepository extends Repository { } if (!backup) { - File reftableDir = new File(getDirectory(), Constants.REFTABLE); + File reftableDir = new File(commonDirectory, Constants.REFTABLE); FileUtils.delete(reftableDir, FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS); } @@ -730,8 +731,10 @@ public class FileRepository extends Repository { @SuppressWarnings("nls") void convertToReftable(boolean writeLogs, boolean backup) throws IOException { - File reftableDir = new File(getDirectory(), Constants.REFTABLE); - File headFile = new File(getDirectory(), Constants.HEAD); + File commonDirectory = getCommonDirectory(); + File directory = getDirectory(); + File reftableDir = new File(commonDirectory, Constants.REFTABLE); + File headFile = new File(directory, Constants.HEAD); if (reftableDir.exists() && FileUtils.hasFiles(reftableDir.toPath())) { throw new IOException(JGitText.get().reftableDirExists); } @@ -739,28 +742,28 @@ public class FileRepository extends Repository { // Ignore return value, as it is tied to temporary newRefs file. FileReftableDatabase.convertFrom(this, writeLogs); - File refsFile = new File(getDirectory(), "refs"); + File refsFile = new File(commonDirectory, "refs"); // non-atomic: remove old data. - File packedRefs = new File(getDirectory(), Constants.PACKED_REFS); - File logsDir = new File(getDirectory(), Constants.LOGS); + File packedRefs = new File(commonDirectory, Constants.PACKED_REFS); + File logsDir = new File(commonDirectory, Constants.LOGS); List<String> additional = getRefDatabase().getAdditionalRefs().stream() .map(Ref::getName).collect(toList()); additional.add(Constants.HEAD); if (backup) { - FileUtils.rename(refsFile, new File(getDirectory(), "refs.old")); + FileUtils.rename(refsFile, new File(commonDirectory, "refs.old")); if (packedRefs.exists()) { - FileUtils.rename(packedRefs, new File(getDirectory(), + FileUtils.rename(packedRefs, new File(commonDirectory, Constants.PACKED_REFS + ".old")); } if (logsDir.exists()) { FileUtils.rename(logsDir, - new File(getDirectory(), Constants.LOGS + ".old")); + new File(commonDirectory, Constants.LOGS + ".old")); } for (String r : additional) { - FileUtils.rename(new File(getDirectory(), r), - new File(getDirectory(), r + ".old")); + FileUtils.rename(new File(commonDirectory, r), + new File(commonDirectory, r + ".old")); } } else { FileUtils.delete(packedRefs, FileUtils.SKIP_MISSING); @@ -770,7 +773,7 @@ public class FileRepository extends Repository { FileUtils.delete(refsFile, FileUtils.RECURSIVE | FileUtils.SKIP_MISSING); for (String r : additional) { - new File(getDirectory(), r).delete(); + new File(commonDirectory, r).delete(); } } @@ -784,7 +787,7 @@ public class FileRepository extends Repository { // Some tools might write directly into .git/refs/heads/BRANCH. By // putting a file here, this fails spectacularly. - FileUtils.createNewFile(new File(refsFile, "heads")); + FileUtils.createNewFile(new File(refsFile, Constants.HEADS)); repoConfig.setString(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null, ConfigConstants.CONFIG_KEY_REF_STORAGE, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java index cf26f8d284..4fafc5a088 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java @@ -1047,7 +1047,7 @@ public class GC { } private void deleteEmptyRefsFolders() throws IOException { - Path refs = repo.getDirectory().toPath().resolve(Constants.R_REFS); + Path refs = repo.getCommonDirectory().toPath().resolve(Constants.R_REFS); // Avoid deleting a folder that was created after the threshold so that concurrent // operations trying to create a reference are not impacted Instant threshold = Instant.now().minus(30, ChronoUnit.SECONDS); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java index 628bf5db0c..8647b3e664 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java @@ -50,7 +50,7 @@ class GcLog { */ GcLog(FileRepository repo) { this.repo = repo; - logFile = new File(repo.getDirectory(), "gc.log"); //$NON-NLS-1$ + logFile = new File(repo.getCommonDirectory(), "gc.log"); //$NON-NLS-1$ lock = new LockFile(logFile); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java index 11d842b246..e8d442b8fb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java @@ -46,7 +46,7 @@ public class InfoAttributesNode extends AttributesNode { FS fs = repository.getFS(); - File attributes = fs.resolve(repository.getDirectory(), + File attributes = fs.resolve(repository.getCommonDirectory(), Constants.INFO_ATTRIBUTES); FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java index a3d74be040..e172f141f5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java @@ -12,7 +12,7 @@ package org.eclipse.jgit.internal.storage.file; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; -import java.util.Arrays; +import java.text.MessageFormat; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.util.NB; @@ -35,7 +35,7 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex { private final UInt24Array positions24; - private final int[] positions32; + private final IntArray positions32; /** * Parallel array to concat(positions24, positions32) with the size of the @@ -45,35 +45,37 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex { * doesn't fit in an int and |value|-1 is the position for the size in the * size64 array e.g. a value of -1 is sizes64[0], -2 = sizes64[1], ... */ - private final int[] sizes32; + private final IntArray sizes32; - private final long[] sizes64; + private final LongArray sizes64; static PackObjectSizeIndex parse(InputStream in) throws IOException { /** Header and version already out of the input */ - IndexInputStreamReader stream = new IndexInputStreamReader(in); - int threshold = stream.readInt(); // minSize - int objCount = stream.readInt(); + byte[] buffer = new byte[8]; + in.readNBytes(buffer, 0, 8); + int threshold = NB.decodeInt32(buffer, 0); // minSize + int objCount = NB.decodeInt32(buffer, 4); if (objCount == 0) { return new EmptyPackObjectSizeIndex(threshold); } - return new PackObjectSizeIndexV1(stream, threshold, objCount); + return new PackObjectSizeIndexV1(in, threshold, objCount); } - private PackObjectSizeIndexV1(IndexInputStreamReader stream, int threshold, + private PackObjectSizeIndexV1(InputStream stream, int threshold, int objCount) throws IOException { this.threshold = threshold; UInt24Array pos24 = null; - int[] pos32 = null; + IntArray pos32 = null; + StreamHelper helper = new StreamHelper(); byte positionEncoding; - while ((positionEncoding = stream.readByte()) != 0) { + while ((positionEncoding = helper.readByte(stream)) != 0) { if (Byte.compareUnsigned(positionEncoding, BITS_24) == 0) { - int sz = stream.readInt(); + int sz = helper.readInt(stream); pos24 = new UInt24Array(stream.readNBytes(sz * 3)); } else if (Byte.compareUnsigned(positionEncoding, BITS_32) == 0) { - int sz = stream.readInt(); - pos32 = stream.readIntArray(sz); + int sz = helper.readInt(stream); + pos32 = IntArray.from(stream, sz); } else { throw new UnsupportedEncodingException( String.format(JGitText.get().unknownPositionEncoding, @@ -81,16 +83,16 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex { } } positions24 = pos24 != null ? pos24 : UInt24Array.EMPTY; - positions32 = pos32 != null ? pos32 : new int[0]; + positions32 = pos32 != null ? pos32 : IntArray.EMPTY; - sizes32 = stream.readIntArray(objCount); - int c64sizes = stream.readInt(); + sizes32 = IntArray.from(stream, objCount); + int c64sizes = helper.readInt(stream); if (c64sizes == 0) { - sizes64 = new long[0]; + sizes64 = LongArray.EMPTY; return; } - sizes64 = stream.readLongArray(c64sizes); - int c128sizes = stream.readInt(); + sizes64 = LongArray.from(stream, c64sizes); + int c128sizes = helper.readInt(stream); if (c128sizes != 0) { // this MUST be 0 (we don't support 128 bits sizes yet) throw new IOException(JGitText.get().unsupportedSizesObjSizeIndex); @@ -102,8 +104,8 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex { int pos = -1; if (!positions24.isEmpty() && idxOffset <= positions24.getLastValue()) { pos = positions24.binarySearch(idxOffset); - } else if (positions32.length > 0 && idxOffset >= positions32[0]) { - int pos32 = Arrays.binarySearch(positions32, idxOffset); + } else if (!positions32.empty() && idxOffset >= positions32.get(0)) { + int pos32 = positions32.binarySearch(idxOffset); if (pos32 >= 0) { pos = pos32 + positions24.size(); } @@ -112,17 +114,17 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex { return -1; } - int objSize = sizes32[pos]; + int objSize = sizes32.get(pos); if (objSize < 0) { int secondPos = Math.abs(objSize) - 1; - return sizes64[secondPos]; + return sizes64.get(secondPos); } return objSize; } @Override public long getObjectCount() { - return (long) positions24.size() + positions32.length; + return (long) positions24.size() + positions32.size(); } @Override @@ -131,69 +133,126 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex { } /** - * Wrapper to read parsed content from the byte stream + * A byte[] that should be interpreted as an int[] */ - private static class IndexInputStreamReader { + private static class IntArray { + private static final IntArray EMPTY = new IntArray(new byte[0]); - private final byte[] buffer = new byte[8]; + private static final int INT_SIZE = 4; - private final InputStream in; + private final byte[] data; - IndexInputStreamReader(InputStream in) { - this.in = in; - } + private final int size; - int readInt() throws IOException { - int n = in.readNBytes(buffer, 0, 4); - if (n < 4) { - throw new IOException(JGitText.get().unableToReadFullInt); + static IntArray from(InputStream in, int ints) throws IOException { + int expectedBytes = ints * INT_SIZE; + byte[] data = in.readNBytes(expectedBytes); + if (data.length < expectedBytes) { + throw new IOException(MessageFormat + .format(JGitText.get().unableToReadFullArray, ints)); } - return NB.decodeInt32(buffer, 0); + return new IntArray(data); + } + + private IntArray(byte[] data) { + this.data = data; + size = data.length / INT_SIZE; } - int[] readIntArray(int intsCount) throws IOException { - if (intsCount == 0) { - return new int[0]; + /** + * Returns position of element in array, -1 if not there + * + * @param needle + * element to look for + * @return position of the element in the array or -1 if not found + */ + int binarySearch(int needle) { + if (size == 0) { + return -1; } + int high = size; + int low = 0; + do { + int mid = (low + high) >>> 1; + int cmp = Integer.compare(needle, get(mid)); + if (cmp < 0) + high = mid; + else if (cmp == 0) { + return mid; + } else + low = mid + 1; + } while (low < high); + return -1; + } - int[] dest = new int[intsCount]; - for (int i = 0; i < intsCount; i++) { - dest[i] = readInt(); + int get(int position) { + if (position < 0 || position >= size) { + throw new IndexOutOfBoundsException(position); } - return dest; + return NB.decodeInt32(data, position * INT_SIZE); } - long readLong() throws IOException { - int n = in.readNBytes(buffer, 0, 8); - if (n < 8) { - throw new IOException(JGitText.get().unableToReadFullInt); + boolean empty() { + return size == 0; + } + + int size() { + return size; + } + } + + /** + * A byte[] that should be interpreted as an long[] + */ + private static class LongArray { + private static final LongArray EMPTY = new LongArray(new byte[0]); + + private static final int LONG_SIZE = 8; // bytes + + private final byte[] data; + + private final int size; + + static LongArray from(InputStream in, int longs) throws IOException { + byte[] data = in.readNBytes(longs * LONG_SIZE); + if (data.length < longs * LONG_SIZE) { + throw new IOException(MessageFormat + .format(JGitText.get().unableToReadFullArray, longs)); } - return NB.decodeInt64(buffer, 0); + return new LongArray(data); } - long[] readLongArray(int longsCount) throws IOException { - if (longsCount == 0) { - return new long[0]; + private LongArray(byte[] data) { + this.data = data; + size = data.length / LONG_SIZE; + } + + long get(int position) { + if (position < 0 || position >= size) { + throw new IndexOutOfBoundsException(position); } + return NB.decodeInt64(data, position * LONG_SIZE); + } + } - long[] dest = new long[longsCount]; - for (int i = 0; i < longsCount; i++) { - dest[i] = readLong(); + private static class StreamHelper { + private final byte[] buffer = new byte[8]; + + int readInt(InputStream in) throws IOException { + int n = in.readNBytes(buffer, 0, 4); + if (n < 4) { + throw new IOException(JGitText.get().unableToReadFullInt); } - return dest; + return NB.decodeInt32(buffer, 0); } - byte readByte() throws IOException { + byte readByte(InputStream in) throws IOException { int n = in.readNBytes(buffer, 0, 1); if (n != 1) { throw new IOException(JGitText.get().cannotReadByte); } return buffer[0]; } - - byte[] readNBytes(int sz) throws IOException { - return in.readNBytes(sz); - } } private static class EmptyPackObjectSizeIndex diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java index 8e57bf9f2f..604868133e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java @@ -16,6 +16,7 @@ package org.eclipse.jgit.internal.storage.file; import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.jgit.lib.Constants.HEAD; import static org.eclipse.jgit.lib.Constants.LOGS; +import static org.eclipse.jgit.lib.Constants.L_LOGS; import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH; import static org.eclipse.jgit.lib.Constants.PACKED_REFS; import static org.eclipse.jgit.lib.Constants.R_HEADS; @@ -124,6 +125,8 @@ public class RefDirectory extends RefDatabase { private final File gitDir; + private final File gitCommonDir; + final File refsDir; final File packedRefsFile; @@ -188,6 +191,7 @@ public class RefDirectory extends RefDatabase { RefDirectory(RefDirectory refDb) { parent = refDb.parent; gitDir = refDb.gitDir; + gitCommonDir = refDb.gitCommonDir; refsDir = refDb.refsDir; logsDir = refDb.logsDir; logsRefsDir = refDb.logsRefsDir; @@ -204,10 +208,11 @@ public class RefDirectory extends RefDatabase { final FS fs = db.getFS(); parent = db; gitDir = db.getDirectory(); - refsDir = fs.resolve(gitDir, R_REFS); - logsDir = fs.resolve(gitDir, LOGS); - logsRefsDir = fs.resolve(gitDir, LOGS + '/' + R_REFS); - packedRefsFile = fs.resolve(gitDir, PACKED_REFS); + gitCommonDir = db.getCommonDirectory(); + refsDir = fs.resolve(gitCommonDir, R_REFS); + logsDir = fs.resolve(gitCommonDir, LOGS); + logsRefsDir = fs.resolve(gitCommonDir, L_LOGS + R_REFS); + packedRefsFile = fs.resolve(gitCommonDir, PACKED_REFS); looseRefs.set(RefList.<LooseRef> emptyList()); packedRefs.set(NO_PACKED_REFS); @@ -1329,7 +1334,12 @@ public class RefDirectory extends RefDatabase { name = name.substring(R_REFS.length()); return new File(refsDir, name); } - return new File(gitDir, name); + // HEAD needs to get resolved from git dir as resolving it from common dir + // would always lead back to current default branch + if (name.equals(HEAD)) { + return new File(gitDir, name); + } + return new File(gitCommonDir, name); } static int levelsIn(String name) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java index 21b5a54eb7..f1888eb90f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java @@ -10,6 +10,8 @@ package org.eclipse.jgit.internal.storage.file; +import static org.eclipse.jgit.lib.Constants.HEAD; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -37,7 +39,9 @@ class ReflogReaderImpl implements ReflogReader { * {@code Ref} name */ ReflogReaderImpl(Repository db, String refname) { - logName = new File(db.getDirectory(), Constants.LOGS + '/' + refname); + File logBaseDir = refname.equals(HEAD) ? db.getDirectory() + : db.getCommonDirectory(); + logName = new File(logBaseDir, Constants.L_LOGS + refname); } /* (non-Javadoc) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java index 5dfb648faa..d232be6276 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java @@ -13,13 +13,17 @@ package org.eclipse.jgit.lib; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BARE; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WORKTREE; +import static org.eclipse.jgit.lib.Constants.CONFIG; import static org.eclipse.jgit.lib.Constants.DOT_GIT; import static org.eclipse.jgit.lib.Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY; import static org.eclipse.jgit.lib.Constants.GIT_CEILING_DIRECTORIES_KEY; +import static org.eclipse.jgit.lib.Constants.GIT_COMMON_DIR_KEY; import static org.eclipse.jgit.lib.Constants.GIT_DIR_KEY; import static org.eclipse.jgit.lib.Constants.GIT_INDEX_FILE_KEY; import static org.eclipse.jgit.lib.Constants.GIT_OBJECT_DIRECTORY_KEY; import static org.eclipse.jgit.lib.Constants.GIT_WORK_TREE_KEY; +import static org.eclipse.jgit.lib.Constants.OBJECTS; +import static org.eclipse.jgit.lib.Constants.GITDIR_FILE; import java.io.File; import java.io.IOException; @@ -70,7 +74,21 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re && ref[7] == ' '; } - private static File getSymRef(File workTree, File dotGit, FS fs) + /** + * Read symbolic reference file + * + * @param workTree + * the work tree path + * @param dotGit + * the .git file + * @param fs + * th FS util + * @return the file read from symbolic reference file + * @throws java.io.IOException + * the dotGit file is invalid reference + * @since 7.0 + */ + static File getSymRef(File workTree, File dotGit, FS fs) throws IOException { byte[] content = IO.readFully(dotGit); if (!isSymRef(content)) { @@ -102,6 +120,8 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re private File gitDir; + private File gitCommonDir; + private File objectDirectory; private List<File> alternateObjectDirectories; @@ -172,6 +192,30 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re } /** + * Set common dir. + * + * @param gitCommonDir + * {@code GIT_COMMON_DIR}, the common repository meta directory. + * @return {@code this} (for chaining calls). + * @since 7.0 + */ + public B setGitCommonDir(File gitCommonDir) { + this.gitCommonDir = gitCommonDir; + this.config = null; + return self(); + } + + /** + * Get common dir. + * + * @return common dir; null if not set. + * @since 7.0 + */ + public File getGitCommonDir() { + return gitCommonDir; + } + + /** * Set the directory storing the repository's objects. * * @param objectDirectory @@ -396,9 +440,9 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re * Read standard Git environment variables and configure from those. * <p> * This method tries to read the standard Git environment variables, such as - * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder - * instance. If an environment variable is set, it overrides the value - * already set in this builder. + * {@code GIT_DIR}, {@code GIT_COMMON_DIR}, {@code GIT_WORK_TREE} etc. to + * configure this builder instance. If an environment variable is set, it + * overrides the value already set in this builder. * * @return {@code this} (for chaining calls). */ @@ -410,9 +454,9 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re * Read standard Git environment variables and configure from those. * <p> * This method tries to read the standard Git environment variables, such as - * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder - * instance. If a property is already set in the builder, the environment - * variable is not used. + * {@code GIT_DIR}, {@code GIT_COMMON_DIR}, {@code GIT_WORK_TREE} etc. to + * configure this builder instance. If a property is already set in the + * builder, the environment variable is not used. * * @param sr * the SystemReader abstraction to access the environment. @@ -425,6 +469,13 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re setGitDir(new File(val)); } + if (getGitCommonDir() == null) { + String val = sr.getenv(GIT_COMMON_DIR_KEY); + if (val != null) { + setGitCommonDir(new File(val)); + } + } + if (getObjectDirectory() == null) { String val = sr.getenv(GIT_OBJECT_DIRECTORY_KEY); if (val != null) @@ -601,6 +652,7 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re public B setup() throws IllegalArgumentException, IOException { requireGitDirOrWorkTree(); setupGitDir(); + setupCommonDir(); setupWorkTree(); setupInternals(); return self(); @@ -658,6 +710,20 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re } /** + * Perform standard common dir initialization. + * + * @throws java.io.IOException + * the repository could not be accessed + * @since 7.0 + */ + protected void setupCommonDir() throws IOException { + // no gitCommonDir? Try to get it from gitDir + if (getGitCommonDir() == null) { + setGitCommonDir(safeFS().getCommonDir(getGitDir())); + } + } + + /** * Perform standard work-tree initialization. * <p> * This is a method typically invoked inside of {@link #setup()}, near the @@ -695,8 +761,12 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re * the repository could not be accessed */ protected void setupInternals() throws IOException { - if (getObjectDirectory() == null && getGitDir() != null) - setObjectDirectory(safeFS().resolve(getGitDir(), Constants.OBJECTS)); + if (getObjectDirectory() == null) { + File commonDir = getGitCommonDir(); + if (commonDir != null) { + setObjectDirectory(safeFS().resolve(commonDir, OBJECTS)); + } + } } /** @@ -723,12 +793,13 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re * the configuration is not available. */ protected Config loadConfig() throws IOException { - if (getGitDir() != null) { + File commonDir = getGitCommonDir(); + if (commonDir != null) { // We only want the repository's configuration file, and not // the user file, as these parameters must be unique to this // repository and not inherited from other files. // - File path = safeFS().resolve(getGitDir(), Constants.CONFIG); + File path = safeFS().resolve(commonDir, CONFIG); FileBasedConfig cfg = new FileBasedConfig(path, safeFS()); try { cfg.load(); @@ -749,8 +820,29 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re // String path = cfg.getString(CONFIG_CORE_SECTION, null, CONFIG_KEY_WORKTREE); - if (path != null) + if (path != null) { return safeFS().resolve(getGitDir(), path).getCanonicalFile(); + } + + /* + * We are in worktree's $GIT_DIR folder + * ".git/worktrees/<worktree-name>" and want to get the working + * tree (checkout) path; so here we have an opposite link in file + * "gitdir" showing to the ".git" file located in the working tree read + * it and convert it to absolute path if it's relative + */ + File gitDirFile = new File(getGitDir(), GITDIR_FILE); + if (gitDirFile.isFile()) { + String workDirPath = new String(IO.readFully(gitDirFile)).trim(); + File workTreeDotGitFile = new File(workDirPath); + if (!workTreeDotGitFile.isAbsolute()) { + workTreeDotGitFile = new File(getGitDir(), workDirPath) + .getCanonicalFile(); + } + if (workTreeDotGitFile != null) { + return workTreeDotGitFile.getParentFile(); + } + } // If core.bare is set, honor its value. Assume workTree is // the parent directory of the repository. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index 1835dc76a2..b9c90bda30 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -273,6 +273,20 @@ public final class Constants { public static final String INFO_REFS = "info/refs"; /** + * Name of heads folder or file in refs. + * + * @since 7.0 + */ + public static final String HEADS = "heads"; + + /** + * Prefix for any log. + * + * @since 7.0 + */ + public static final String L_LOGS = LOGS + "/"; + + /** * Info alternates file (goes under OBJECTS) * @since 5.5 */ @@ -358,6 +372,14 @@ public final class Constants { public static final String GIT_DIR_KEY = "GIT_DIR"; /** + * The environment variable that tells us which directory is the common + * ".git" directory. + * + * @since 7.0 + */ + public static final String GIT_COMMON_DIR_KEY = "GIT_COMMON_DIR"; + + /** * The environment variable that tells us which directory is the working * directory. */ @@ -459,6 +481,36 @@ public final class Constants { public static final String GITDIR = "gitdir: "; /** + * Name of the file (inside gitDir) that references the worktree's .git + * file (opposite link). + * + * .git/worktrees/<worktree-name>/gitdir + * + * A text file containing the absolute path back to the .git file that + * points here. This file is used to verify if the linked repository has been + * manually removed in which case this directory is no longer needed. + * The modification time (mtime) of this file should be updated each time + * the linked repository is accessed. + * + * @since 7.0 + */ + public static final String GITDIR_FILE = "gitdir"; + + /** + * Name of the file (inside gitDir) that has reference to $GIT_COMMON_DIR. + * + * .git/worktrees/<worktree-name>/commondir + * + * If this file exists, $GIT_COMMON_DIR will be set to the path specified in + * this file unless it is explicitly set. If the specified path is relative, + * it is relative to $GIT_DIR. The repository with commondir is incomplete + * without the repository pointed by "commondir". + * + * @since 7.0 + */ + public static final String COMMONDIR_FILE = "commondir"; + + /** * Name of the folder (inside gitDir) where submodules are stored * * @since 3.6 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java index 8e965c5e9d..a99c64701f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java @@ -639,7 +639,7 @@ public class IndexDiff { // submodule repository in .git/modules doesn't // exist yet it isn't "missing". File gitDir = new File( - new File(repository.getDirectory(), + new File(repository.getCommonDirectory(), Constants.MODULES), subRepoPath); if (!gitDir.isDirectory()) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index 4722e29b4c..9dde99fada 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -113,9 +113,12 @@ public abstract class Repository implements AutoCloseable { final AtomicLong closedAt = new AtomicLong(); - /** Metadata directory holding the repository's critical files. */ + /** $GIT_DIR: metadata directory holding the repository's critical files. */ private final File gitDir; + /** $GIT_COMMON_DIR: metadata directory holding the common repository's critical files. */ + private final File gitCommonDir; + /** File abstraction used to resolve paths. */ private final FS fs; @@ -137,6 +140,7 @@ public abstract class Repository implements AutoCloseable { */ protected Repository(BaseRepositoryBuilder options) { gitDir = options.getGitDir(); + gitCommonDir = options.getGitCommonDir(); fs = options.getFS(); workTree = options.getWorkTree(); indexFile = options.getIndexFile(); @@ -220,6 +224,16 @@ public abstract class Repository implements AutoCloseable { public abstract String getIdentifier(); /** + * Get common dir. + * + * @return $GIT_COMMON_DIR: local common metadata directory; + * @since 7.0 + */ + public File getCommonDirectory() { + return gitCommonDir; + } + + /** * Get the object database which stores this repository's data. * * @return the object database which stores this repository's data. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java index 6288447a8d..18366541da 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java @@ -450,10 +450,21 @@ public class RepositoryCache { * Git directory. */ public static boolean isGitRepository(File dir, FS fs) { - return fs.resolve(dir, Constants.OBJECTS).exists() - && fs.resolve(dir, "refs").exists() //$NON-NLS-1$ - && (fs.resolve(dir, Constants.REFTABLE).exists() - || isValidHead(new File(dir, Constants.HEAD))); + // check if common-dir available or fallback to git-dir + File commonDir; + try { + commonDir = fs.getCommonDir(dir); + } catch (IOException e) { + commonDir = null; + } + if (commonDir == null) { + commonDir = dir; + } + return fs.resolve(commonDir, Constants.OBJECTS).exists() + && fs.resolve(commonDir, "refs").exists() //$NON-NLS-1$ + && (fs.resolve(commonDir, Constants.REFTABLE).exists() + || isValidHead( + new File(commonDir, Constants.HEAD))); } private static boolean isValidHead(File head) { @@ -496,15 +507,31 @@ public class RepositoryCache { * null if there is no suitable match. */ public static File resolve(File directory, FS fs) { - if (isGitRepository(directory, fs)) + // the folder itself + if (isGitRepository(directory, fs)) { return directory; - if (isGitRepository(new File(directory, Constants.DOT_GIT), fs)) - return new File(directory, Constants.DOT_GIT); - - final String name = directory.getName(); - final File parent = directory.getParentFile(); - if (isGitRepository(new File(parent, name + Constants.DOT_GIT_EXT), fs)) - return new File(parent, name + Constants.DOT_GIT_EXT); + } + // the .git subfolder or file (reference) + File dotDir = new File(directory, Constants.DOT_GIT); + if (dotDir.isFile()) { + try { + File refDir = BaseRepositoryBuilder.getSymRef(directory, + dotDir, fs); + if (refDir != null && isGitRepository(refDir, fs)) { + return refDir; + } + } catch (IOException ignored) { + // Continue searching if gitdir ref isn't found + } + } else if (isGitRepository(dotDir, fs)) { + return dotDir; + } + // the folder extended with .git (bare) + File bareDir = new File(directory.getParentFile(), + directory.getName() + Constants.DOT_GIT_EXT); + if (isGitRepository(bareDir, fs)) { + return bareDir; + } return null; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java index 0fc9710ecb..f77b04110d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java @@ -254,6 +254,12 @@ public class TransportGitSsh extends SshTransport implements PackTransport { pb.environment().put(Constants.GIT_DIR_KEY, directory.getPath()); } + File commonDirectory = local != null ? local.getCommonDirectory() + : null; + if (commonDirectory != null) { + pb.environment().put(Constants.GIT_COMMON_DIR_KEY, + commonDirectory.getPath()); + } return pb; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java index 3a06ce5b63..1b9431ce6e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java @@ -225,6 +225,7 @@ class TransportLocal extends Transport implements PackTransport { env.remove("GIT_CONFIG"); //$NON-NLS-1$ env.remove("GIT_CONFIG_PARAMETERS"); //$NON-NLS-1$ env.remove("GIT_DIR"); //$NON-NLS-1$ + env.remove("GIT_COMMON_DIR"); //$NON-NLS-1$ env.remove("GIT_WORK_TREE"); //$NON-NLS-1$ env.remove("GIT_GRAFT_FILE"); //$NON-NLS-1$ env.remove("GIT_INDEX_FILE"); //$NON-NLS-1$ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index 73a3ddaae7..95e9964192 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -498,6 +498,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { filterProcessBuilder.directory(repository.getWorkTree()); filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY, repository.getDirectory().getAbsolutePath()); + filterProcessBuilder.environment().put(Constants.GIT_COMMON_DIR_KEY, + repository.getCommonDirectory().getAbsolutePath()); ExecutionResult result; try { result = fs.execute(filterProcessBuilder, in); @@ -1332,7 +1334,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { IgnoreNode infoExclude = new IgnoreNodeWithParent( coreExclude); - File exclude = fs.resolve(repository.getDirectory(), + File exclude = fs.resolve(repository.getCommonDirectory(), Constants.INFO_EXCLUDE); if (fs.exists(exclude)) { loadRulesFromFile(infoExclude, exclude); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index a8e1dae10e..6933a6c567 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -2042,6 +2042,8 @@ public abstract class FS { environment.put(Constants.GIT_DIR_KEY, repository.getDirectory().getAbsolutePath()); if (!repository.isBare()) { + environment.put(Constants.GIT_COMMON_DIR_KEY, + repository.getCommonDirectory().getAbsolutePath()); environment.put(Constants.GIT_WORK_TREE_KEY, repository.getWorkTree().getAbsolutePath()); } @@ -2137,7 +2139,7 @@ public abstract class FS { case "post-receive": //$NON-NLS-1$ case "post-update": //$NON-NLS-1$ case "push-to-checkout": //$NON-NLS-1$ - return repository.getDirectory(); + return repository.getCommonDirectory(); default: return repository.getWorkTree(); } @@ -2150,7 +2152,7 @@ public abstract class FS { if (hooksDir != null) { return new File(hooksDir); } - File dir = repository.getDirectory(); + File dir = repository.getCommonDirectory(); return dir == null ? null : new File(dir, Constants.HOOKS); } @@ -2578,6 +2580,33 @@ public abstract class FS { } /** + * Get common dir path. + * + * @param dir + * the .git folder + * @return common dir path + * @throws IOException + * if commondir file can't be read + * + * @since 7.0 + */ + public File getCommonDir(File dir) throws IOException { + // first the GIT_COMMON_DIR is same as GIT_DIR + File commonDir = dir; + // now check if commondir file exists (e.g. worktree repository) + File commonDirFile = new File(dir, Constants.COMMONDIR_FILE); + if (commonDirFile.isFile()) { + String commonDirPath = new String(IO.readFully(commonDirFile)) + .trim(); + commonDir = new File(commonDirPath); + if (!commonDir.isAbsolute()) { + commonDir = new File(dir, commonDirPath).getCanonicalFile(); + } + } + return commonDir; + } + + /** * This runnable will consume an input stream's content into an output * stream as soon as it gets available. * <p> |