Skip to content

Commit

Permalink
Add support for multiple linked worktrees
Browse files Browse the repository at this point in the history
Until now, the working tree was forced to be the parent of a found
GIT_DIR. This is true for plain repositories, but fails for worktrees
and submodules. The GIT_DIR for those special repositories resides
inside the main now.

This is not supported by JGit out-of-the-box as of now. So we try to
find the `commondir` of a worktree and set it as the repository‘s
GIT_DIR.

See https://git-scm.com/docs/gitrepository-layout for further
information.

See #1
Closes #98
  • Loading branch information
koraktor committed Feb 5, 2019
1 parent 3900d64 commit 0ad70b1
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2012-2018, Sebastian Staudt
* Copyright (c) 2012-2019, Sebastian Staudt
* 2015, Kay Hannay
*/

Expand Down Expand Up @@ -38,9 +38,10 @@
import com.github.koraktor.mavanagaiata.git.GitTag;
import com.github.koraktor.mavanagaiata.git.GitTagDescription;

import static java.util.Collections.min;
import static java.util.Comparator.comparingInt;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import static java.util.Collections.*;
import static java.util.Comparator.*;
import static org.apache.commons.io.FileUtils.*;
import static org.eclipse.jgit.lib.Constants.*;

/**
* Wrapper around JGit's {@link Repository} object to represent a Git
Expand Down Expand Up @@ -94,11 +95,37 @@ final void buildRepository(File workTree, File gitDir) throws GitRepositoryExcep

FileRepositoryBuilder repositoryBuilder = getRepositoryBuilder();
if (gitDir == null) {
if (repositoryBuilder.findGitDir(workTree).getGitDir() == null) {
File foundGitDir = repositoryBuilder.findGitDir(workTree).getGitDir();
if (foundGitDir == null) {
throw new GitRepositoryException(workTree + " is not inside a Git repository. Please specify the GIT_DIR separately.");
}

repositoryBuilder.setWorkTree(repositoryBuilder.getGitDir().getParentFile());
try {
if (!directoryContains(workTree, foundGitDir)) {
if (directoryContains(foundGitDir.getParentFile(), workTree)) {
repositoryBuilder.setGitDir(foundGitDir);
repositoryBuilder.setWorkTree(foundGitDir.getParentFile());
} else {
File commonDir = new File(foundGitDir, "commondir");
String realGitDirPath = readFileToString(commonDir, "UTF-8").trim();

File realGitDir = new File(foundGitDir, realGitDirPath);
if (!realGitDir.exists()) {
realGitDir = new File(realGitDirPath);
}

if (realGitDir.exists()) {
repositoryBuilder.setGitDir(realGitDir);
repositoryBuilder.setWorkTree(workTree);
}
}
} else {
repositoryBuilder.setGitDir(foundGitDir);
repositoryBuilder.setWorkTree(workTree);
}
} catch (IOException e) {
throw new GitRepositoryException("Failure while resolving GIT_DIR.", e);
}
} else {
repositoryBuilder.setGitDir(gitDir);
repositoryBuilder.setWorkTree(workTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
* This code is free software; you can redistribute it and/or modify it under
* the terms of the new BSD License.
*
* Copyright (c) 2012-2018, Sebastian Staudt
* Copyright (c) 2012-2019, Sebastian Staudt
*/

package com.github.koraktor.mavanagaiata.git.jgit;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -18,6 +19,8 @@
import java.util.Random;
import java.util.TimeZone;

import org.apache.commons.io.FileUtils;

import org.mockito.InOrder;

import org.eclipse.jgit.errors.CorruptObjectException;
Expand Down Expand Up @@ -69,11 +72,15 @@ void setup() {
@DisplayName("should be able to create based on a worktree")
@Test
void testCreateWithWorkTree() throws Exception {
File workTree = mock(File.class);
when(workTree.exists()).thenReturn(true);
File workTree = File.createTempFile("workTree", null);
if (workTree.delete() && workTree.mkdir()) {
workTree.deleteOnExit();
}

File gitDir = mock(File.class);
when(gitDir.getParentFile()).thenReturn(workTree);
File gitDir = new File(workTree, ".git");
if (gitDir.mkdir()) {
gitDir.deleteOnExit();
}

FileRepositoryBuilder repoBuilder = mock(FileRepositoryBuilder.class);
when(repoBuilder.findGitDir(any())).thenReturn(repoBuilder);
Expand Down Expand Up @@ -114,13 +121,24 @@ void testCreateWithWorkTreeAndGitDir() throws Exception {
@DisplayName("should be able to create based on a subdirectory of a worktree")
@Test
void testCreateWithWorkTreeChild() throws Exception {
File workTree = mock(File.class);
File workTree = File.createTempFile("workTree", null);
if (workTree.delete() && workTree.mkdir()) {
workTree.deleteOnExit();
}

File workTreeChild = mock(File.class);
when(workTreeChild.exists()).thenReturn(true);
File workTreeChild = new File(workTree, "child");
if (workTreeChild.mkdir()) {
workTreeChild.deleteOnExit();
}

File gitDir = new File(workTree, ".git");
if (gitDir.mkdir()) {
gitDir.deleteOnExit();
}

FileRepositoryBuilder repoBuilder = mock(FileRepositoryBuilder.class, RETURNS_DEEP_STUBS);
when(repoBuilder.getGitDir().getParentFile()).thenReturn(workTree);
when(repoBuilder.findGitDir(any())).thenReturn(repoBuilder);
when(repoBuilder.getGitDir()).thenReturn(gitDir);

JGitRepository repository = spy(new JGitRepository());
when(repository.getRepositoryBuilder()).thenReturn(repoBuilder);
Expand All @@ -132,6 +150,40 @@ void testCreateWithWorkTreeChild() throws Exception {
inOrder.verify(repoBuilder).setWorkTree(workTree);
}

@DisplayName("should be able to create based on a linked worktree")
@Test
void testCreateWithLinkedWorktree() throws Exception {
File realGitDir = File.createTempFile(".git", null);
if (realGitDir.delete() && realGitDir.mkdir()) {
realGitDir.deleteOnExit();
}

File gitDir = new File(realGitDir, ".git/worktrees/test");
if (gitDir.mkdir()) {
gitDir.deleteOnExit();
}

File workTree = File.createTempFile("workTree", null);
if (workTree.delete() && workTree.mkdir()) {
workTree.deleteOnExit();
}

FileUtils.writeStringToFile(new File(gitDir, "commondir"), realGitDir.getAbsolutePath(), Charset.forName("UTF-8"));

FileRepositoryBuilder repoBuilder = mock(FileRepositoryBuilder.class, RETURNS_DEEP_STUBS);
when(repoBuilder.findGitDir(any())).thenReturn(repoBuilder);
when(repoBuilder.getGitDir()).thenReturn(gitDir);

JGitRepository repository = spy(new JGitRepository());
when(repository.getRepositoryBuilder()).thenReturn(repoBuilder);

repository.buildRepository(workTree, null);

InOrder inOrder = inOrder(repoBuilder);
inOrder.verify(repoBuilder).setGitDir(realGitDir);
inOrder.verify(repoBuilder).setWorkTree(workTree);
}

@DisplayName("should throw a specific error when unable to initialize")
@Test
void testBuildRepositoryFailure() throws Exception {
Expand Down

0 comments on commit 0ad70b1

Please sign in to comment.