[git] IJPL-84816 Support shallow clone at "Get from Version Control" screen

GitOrigin-RevId: 27e27e722ce57d5807e7081bedbaf4f487077962
This commit is contained in:
Ilia.Shulgin
2024-10-03 09:26:33 +02:00
committed by intellij-monorepo-bot
parent 13a6cb55f8
commit ca7bfcfe06
11 changed files with 201 additions and 33 deletions

View File

@@ -609,7 +609,7 @@ a:com.intellij.dvcs.ui.DvcsCloneDialogComponent
- doValidateAll():java.util.List - doValidateAll():java.util.List
- f:getDirectory():java.lang.String - f:getDirectory():java.lang.String
- pf:getErrorComponent():com.intellij.util.ui.components.BorderLayoutPanel - pf:getErrorComponent():com.intellij.util.ui.components.BorderLayoutPanel
- pf:getMainPanel():javax.swing.JPanel - pf:getMainPanel():com.intellij.openapi.ui.DialogPanel
- getPreferredFocusedComponent():javax.swing.JComponent - getPreferredFocusedComponent():javax.swing.JComponent
- f:getProject():com.intellij.openapi.project.Project - f:getProject():com.intellij.openapi.project.Project
- pf:getRememberedInputs():com.intellij.dvcs.DvcsRememberedInputs - pf:getRememberedInputs():com.intellij.dvcs.DvcsRememberedInputs

View File

@@ -7,6 +7,7 @@ import com.intellij.dvcs.ui.CloneDvcsValidationUtils.sanitizeCloneUrl
import com.intellij.dvcs.ui.DvcsBundle.message import com.intellij.dvcs.ui.DvcsBundle.message
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogPanel
import com.intellij.openapi.ui.TextFieldWithBrowseButton import com.intellij.openapi.ui.TextFieldWithBrowseButton
import com.intellij.openapi.ui.ValidationInfo import com.intellij.openapi.ui.ValidationInfo
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
@@ -18,22 +19,26 @@ import com.intellij.ui.DocumentAdapter
import com.intellij.ui.TextFieldWithHistory import com.intellij.ui.TextFieldWithHistory
import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.BottomGap import com.intellij.ui.dsl.builder.BottomGap
import com.intellij.ui.dsl.builder.Panel
import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.panel
import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.ui.JBEmptyBorder import com.intellij.util.ui.JBEmptyBorder
import com.intellij.util.ui.UIUtil import com.intellij.util.ui.UIUtil
import com.intellij.util.ui.components.BorderLayoutPanel import com.intellij.util.ui.components.BorderLayoutPanel
import org.jetbrains.annotations.ApiStatus
import javax.swing.JComponent import javax.swing.JComponent
import javax.swing.JPanel import javax.swing.JPanel
import javax.swing.event.DocumentEvent import javax.swing.event.DocumentEvent
abstract class DvcsCloneDialogComponent(var project: Project, abstract class DvcsCloneDialogComponent @ApiStatus.Internal constructor(
private var vcsDirectoryName: String, var project: Project,
protected val rememberedInputs: DvcsRememberedInputs, private var vcsDirectoryName: String,
private val dialogStateListener: VcsCloneDialogComponentStateListener) protected val rememberedInputs: DvcsRememberedInputs,
: VcsCloneComponent, VcsCloneComponent.WithSettableUrl { private val dialogStateListener: VcsCloneDialogComponentStateListener,
protected val mainPanel: JPanel @ApiStatus.Internal
protected val mainPanelCustomizer: MainPanelCustomizer?,
) : VcsCloneComponent, VcsCloneComponent.WithSettableUrl {
protected val mainPanel: DialogPanel
private val urlEditor = TextFieldWithHistory() private val urlEditor = TextFieldWithHistory()
private val directoryField = TextFieldWithBrowseButton() private val directoryField = TextFieldWithBrowseButton()
private val cloneDirectoryChildHandle = FilePathDocumentChildPathHandle private val cloneDirectoryChildHandle = FilePathDocumentChildPathHandle
@@ -41,6 +46,13 @@ abstract class DvcsCloneDialogComponent(var project: Project,
protected lateinit var errorComponent: BorderLayoutPanel protected lateinit var errorComponent: BorderLayoutPanel
constructor(
project: Project,
vcsDirectoryName: String,
rememberedInputs: DvcsRememberedInputs,
dialogStateListener: VcsCloneDialogComponentStateListener,
): this(project, vcsDirectoryName, rememberedInputs, dialogStateListener, null)
init { init {
directoryField.addBrowseFolderListener(project, FileChooserDescriptorFactory.createSingleFolderDescriptor() directoryField.addBrowseFolderListener(project, FileChooserDescriptorFactory.createSingleFolderDescriptor()
.withTitle(message("clone.destination.directory.browser.title")) .withTitle(message("clone.destination.directory.browser.title"))
@@ -48,14 +60,23 @@ abstract class DvcsCloneDialogComponent(var project: Project,
.withShowFileSystemRoots(true) .withShowFileSystemRoots(true)
.withHideIgnored(false)) .withHideIgnored(false))
mainPanel = panel { mainPanel = panel {
row(VcsBundle.message("vcs.common.labels.url")) { cell(urlEditor).align(AlignX.FILL) } row(VcsBundle.message("vcs.common.labels.url")) {
row(VcsBundle.message("vcs.common.labels.directory")) { cell(directoryField).align(AlignX.FILL) } cell(urlEditor).align(AlignX.FILL).validationOnApply {
.bottomGap(BottomGap.SMALL) CloneDvcsValidationUtils.checkRepositoryURL(it, it.text.trim())
}
}
row(VcsBundle.message("vcs.common.labels.directory")) {
cell(directoryField).align(AlignX.FILL).validationOnApply {
CloneDvcsValidationUtils.checkDirectory(it.text, it.textField)
}
}.bottomGap(BottomGap.SMALL)
mainPanelCustomizer?.configure(this)
row { row {
errorComponent = BorderLayoutPanel(UIUtil.DEFAULT_HGAP, 0) errorComponent = BorderLayoutPanel(UIUtil.DEFAULT_HGAP, 0)
cell(errorComponent).align(AlignX.FILL) cell(errorComponent).align(AlignX.FILL)
} }
} }
mainPanel.registerValidators(this)
val insets = UIUtil.PANEL_REGULAR_INSETS val insets = UIUtil.PANEL_REGULAR_INSETS
mainPanel.border = JBEmptyBorder(insets.top / 2, insets.left, insets.bottom, insets.right) mainPanel.border = JBEmptyBorder(insets.top / 2, insets.left, insets.bottom, insets.right)
@@ -75,17 +96,14 @@ abstract class DvcsCloneDialogComponent(var project: Project,
return StringUtil.trimEnd(ClonePathProvider.relativeDirectoryPathForVcsUrl(project, url), vcsDirectoryName) return StringUtil.trimEnd(ClonePathProvider.relativeDirectoryPathForVcsUrl(project, url), vcsDirectoryName)
} }
override fun getView() = mainPanel override fun getView(): JPanel = mainPanel
override fun isOkEnabled(): Boolean { override fun isOkEnabled(): Boolean {
return false return false
} }
override fun doValidateAll(): List<ValidationInfo> { override fun doValidateAll(): List<ValidationInfo> {
val list = ArrayList<ValidationInfo>() return mainPanel.validateAll()
ContainerUtil.addIfNotNull(list, CloneDvcsValidationUtils.checkDirectory(directoryField.text, directoryField.textField))
ContainerUtil.addIfNotNull(list, CloneDvcsValidationUtils.checkRepositoryURL(urlEditor, urlEditor.text.trim()))
return list
} }
abstract override fun doClone(listener: CheckoutProvider.Listener) abstract override fun doClone(listener: CheckoutProvider.Listener)
@@ -107,4 +125,9 @@ abstract class DvcsCloneDialogComponent(var project: Project,
protected fun updateOkActionState(dialogStateListener: VcsCloneDialogComponentStateListener) { protected fun updateOkActionState(dialogStateListener: VcsCloneDialogComponentStateListener) {
dialogStateListener.onOkActionEnabled(isOkActionEnabled()) dialogStateListener.onOkActionEnabled(isOkActionEnabled())
} }
@ApiStatus.Internal
abstract class MainPanelCustomizer {
abstract fun configure(panel: Panel)
}
} }

View File

@@ -620,6 +620,8 @@
description="Enable embedded pinentry application for unlock GPG private key while Git perform commit signing. For remote dev (unix backend) and WSL."/> description="Enable embedded pinentry application for unlock GPG private key while Git perform commit signing. For remote dev (unix backend) and WSL."/>
<registryKey key="git.read.branches.from.disk" defaultValue="false" <registryKey key="git.read.branches.from.disk" defaultValue="false"
description="When enabled, read the '.git/refs' directory contents. When disabled, delegate to 'git branch' call."/> description="When enabled, read the '.git/refs' directory contents. When disabled, delegate to 'git branch' call."/>
<registryKey key="git.clone.shallow" defaultValue="false"
description="When enabled, allows shallow cloning of the git repository"/>
<search.projectOptionsTopHitProvider implementation="git4idea.config.GitOptionsTopHitProvider"/> <search.projectOptionsTopHitProvider implementation="git4idea.config.GitOptionsTopHitProvider"/>
<vcs name="Git" vcsClass="git4idea.GitVcs" displayName="Git" administrativeAreaName=".git"/> <vcs name="Git" vcsClass="git4idea.GitVcs" displayName="Git" administrativeAreaName=".git"/>

View File

@@ -756,6 +756,9 @@ gpg.pinentry.title=Unlock GPG Private Key
gpg.pinentry.default.description=Please enter the passphrase to unlock the GPG private key: gpg.pinentry.default.description=Please enter the passphrase to unlock the GPG private key:
clone.dialog.checking.git.version=Checking Git version\u2026 clone.dialog.checking.git.version=Checking Git version\u2026
clone.dialog.shallow.clone=Shallow clone with a history truncated to
clone.dialog.shallow.clone.depth=commits
push.dialog.push.tags=Push &tags push.dialog.push.tags=Push &tags
push.dialog.push.tags.combo.current.branch=Current Branch push.dialog.push.tags.combo.current.branch=Current Branch
push.dialog.push.tags.combo.all=All push.dialog.push.tags.combo.all=All

View File

@@ -27,10 +27,7 @@ import com.intellij.openapi.wm.impl.welcomeScreen.cloneableProjects.CloneablePro
import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtil;
import git4idea.GitUtil; import git4idea.GitUtil;
import git4idea.GitVcs; import git4idea.GitVcs;
import git4idea.commands.Git; import git4idea.commands.*;
import git4idea.commands.GitCommandResult;
import git4idea.commands.GitLineHandlerListener;
import git4idea.commands.GitStandardProgressAnalyzer;
import git4idea.i18n.GitBundle; import git4idea.i18n.GitBundle;
import git4idea.ui.GitCloneDialogComponent; import git4idea.ui.GitCloneDialogComponent;
import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NonNls;
@@ -82,9 +79,22 @@ public final class GitCheckoutProvider extends CheckoutProviderEx {
directoryName, parentDirectory); directoryName, parentDirectory);
} }
public static void clone(final @NotNull Project project,
final @NotNull Git git,
final Listener listener,
final VirtualFile destinationParent,
final String sourceRepositoryURL,
final String directoryName,
final String parentDirectory) {
clone(project, git, listener, destinationParent, sourceRepositoryURL, directoryName, parentDirectory, null);
}
public static void clone(final @NotNull Project project, final @NotNull Git git, final Listener listener, public static void clone(final @NotNull Project project, final @NotNull Git git, final Listener listener,
final VirtualFile destinationParent, final String sourceRepositoryURL, final VirtualFile destinationParent,
final String directoryName, final String parentDirectory) { final String sourceRepositoryURL,
final String directoryName,
final String parentDirectory,
final GitShallowCloneOptions shallowCloneOptions) {
String projectAbsolutePath = Paths.get(parentDirectory, directoryName).toAbsolutePath().toString(); String projectAbsolutePath = Paths.get(parentDirectory, directoryName).toAbsolutePath().toString();
String projectPath = FileUtilRt.toSystemIndependentName(projectAbsolutePath); String projectPath = FileUtilRt.toSystemIndependentName(projectAbsolutePath);
@@ -109,7 +119,7 @@ public final class GitCheckoutProvider extends CheckoutProviderEx {
GitCommandResult result; GitCommandResult result;
try { try {
result = git.clone(project, new File(parentDirectory), sourceRepositoryURL, directoryName, progressListener); result = git.clone(project, new File(parentDirectory), sourceRepositoryURL, directoryName, shallowCloneOptions, progressListener);
} }
catch (Exception e) { catch (Exception e) {
if (listener instanceof GitCheckoutListener) { if (listener instanceof GitCheckoutListener) {
@@ -143,13 +153,25 @@ public final class GitCheckoutProvider extends CheckoutProviderEx {
CloneableProjectsService.getInstance().runCloneTask(projectPath, cloneTask); CloneableProjectsService.getInstance().runCloneTask(projectPath, cloneTask);
} }
public static boolean doClone(@NotNull Project project, @NotNull Git git, public static boolean doClone(@NotNull Project project,
@NotNull String directoryName, @NotNull String parentDirectory, @NotNull String sourceRepositoryURL) { @NotNull Git git,
@NotNull String directoryName,
@NotNull String parentDirectory,
@NotNull String sourceRepositoryURL) {
return doClone(project, git, directoryName, parentDirectory, sourceRepositoryURL, null);
}
public static boolean doClone(@NotNull Project project,
@NotNull Git git,
@NotNull String directoryName,
@NotNull String parentDirectory,
@NotNull String sourceRepositoryURL,
@Nullable GitShallowCloneOptions shallowCloneOptions) {
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
indicator.setIndeterminate(false); indicator.setIndeterminate(false);
GitLineHandlerListener progressListener = GitStandardProgressAnalyzer.createListener(indicator); GitLineHandlerListener progressListener = GitStandardProgressAnalyzer.createListener(indicator);
GitCommandResult result = git.clone(project, new File(parentDirectory), sourceRepositoryURL, directoryName, progressListener); GitCommandResult result = git.clone(project, new File(parentDirectory), sourceRepositoryURL, directoryName, shallowCloneOptions, progressListener);
if (result.success()) { if (result.success()) {
return true; return true;
} }

View File

@@ -74,7 +74,20 @@ public interface Git {
@Nullable List<String> relativePaths) throws VcsException; @Nullable List<String> relativePaths) throws VcsException;
@NotNull @NotNull
GitCommandResult clone(@Nullable Project project, @NotNull File parentDirectory, @NotNull String url, @NotNull String clonedDirectoryName, default GitCommandResult clone(@Nullable Project project,
@NotNull File parentDirectory,
@NotNull String url,
@NotNull String clonedDirectoryName,
GitLineHandlerListener @NotNull ... progressListeners) {
return clone(project, parentDirectory, url, clonedDirectoryName, null, progressListeners);
}
@NotNull
GitCommandResult clone(@Nullable Project project,
@NotNull File parentDirectory,
@NotNull String url,
@NotNull String clonedDirectoryName,
@Nullable GitShallowCloneOptions shallowCloneOptions,
GitLineHandlerListener @NotNull ... progressListeners); GitLineHandlerListener @NotNull ... progressListeners);
@NotNull @NotNull

View File

@@ -180,8 +180,12 @@ public class GitImpl extends GitImplBase {
} }
@Override @Override
public @NotNull GitCommandResult clone(final @Nullable Project project, final @NotNull File parentDirectory, final @NotNull String url, public @NotNull GitCommandResult clone(final @Nullable Project project,
final @NotNull String clonedDirectoryName, final GitLineHandlerListener @NotNull ... listeners) { final @NotNull File parentDirectory,
final @NotNull String url,
final @NotNull String clonedDirectoryName,
final @Nullable GitShallowCloneOptions shallowCloneOptions,
final GitLineHandlerListener @NotNull ... listeners) {
return runCommand(() -> { return runCommand(() -> {
// do not use per-project executable for 'clone' command // do not use per-project executable for 'clone' command
Project defaultProject = ProjectManager.getInstance().getDefaultProject(); Project defaultProject = ProjectManager.getInstance().getDefaultProject();
@@ -195,6 +199,12 @@ public class GitImpl extends GitImplBase {
AdvancedSettings.getBoolean("git.clone.recurse.submodules")) { AdvancedSettings.getBoolean("git.clone.recurse.submodules")) {
handler.addParameters("--recurse-submodules"); handler.addParameters("--recurse-submodules");
} }
if (shallowCloneOptions != null) {
Integer depth = shallowCloneOptions.getDepth();
if (depth != null) {
handler.addParameters("--depth=" + depth);
}
}
handler.addParameters(url); handler.addParameters(url);
handler.endOptions(); handler.endOptions();
handler.addParameters(clonedDirectoryName); handler.addParameters(clonedDirectoryName);

View File

@@ -0,0 +1,4 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package git4idea.commands
class GitShallowCloneOptions(val depth: Int?)

View File

@@ -4,23 +4,28 @@ package git4idea.ui
import com.intellij.application.subscribe import com.intellij.application.subscribe
import com.intellij.dvcs.ui.CloneDvcsValidationUtils import com.intellij.dvcs.ui.CloneDvcsValidationUtils
import com.intellij.dvcs.ui.DvcsCloneDialogComponent import com.intellij.dvcs.ui.DvcsCloneDialogComponent
import com.intellij.ide.IdeBundle.message
import com.intellij.openapi.application.ApplicationActivationListener import com.intellij.openapi.application.ApplicationActivationListener
import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.invokeAndWaitIfNeeded import com.intellij.openapi.application.invokeAndWaitIfNeeded
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.vcs.CheckoutProvider import com.intellij.openapi.vcs.CheckoutProvider
import com.intellij.openapi.vcs.VcsBundle import com.intellij.openapi.vcs.VcsBundle
import com.intellij.openapi.vcs.VcsNotifier import com.intellij.openapi.vcs.VcsNotifier
import com.intellij.openapi.vcs.ui.cloneDialog.VcsCloneDialogComponentStateListener import com.intellij.openapi.vcs.ui.cloneDialog.VcsCloneDialogComponentStateListener
import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.wm.IdeFrame import com.intellij.openapi.wm.IdeFrame
import com.intellij.ui.dsl.builder.*
import com.intellij.util.Alarm import com.intellij.util.Alarm
import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.concurrency.annotations.RequiresEdt
import git4idea.GitNotificationIdsHolder.Companion.CLONE_ERROR_UNABLE_TO_CREATE_DESTINATION_DIR import git4idea.GitNotificationIdsHolder.Companion.CLONE_ERROR_UNABLE_TO_CREATE_DESTINATION_DIR
import git4idea.GitUtil import git4idea.GitUtil
import git4idea.checkout.GitCheckoutProvider import git4idea.checkout.GitCheckoutProvider
import git4idea.commands.Git import git4idea.commands.Git
import git4idea.commands.GitShallowCloneOptions
import git4idea.config.* import git4idea.config.*
import git4idea.i18n.GitBundle import git4idea.i18n.GitBundle
import git4idea.remote.GitRememberedInputs import git4idea.remote.GitRememberedInputs
@@ -32,7 +37,8 @@ class GitCloneDialogComponent(project: Project,
DvcsCloneDialogComponent(project, DvcsCloneDialogComponent(project,
GitUtil.DOT_GIT, GitUtil.DOT_GIT,
GitRememberedInputs.getInstance(), GitRememberedInputs.getInstance(),
dialogStateListener) { dialogStateListener,
GitCloneDialogMainPanelCustomizer()) {
private val LOG = Logger.getInstance(GitCloneDialogComponent::class.java) private val LOG = Logger.getInstance(GitCloneDialogComponent::class.java)
private val executableManager get() = GitExecutableManager.getInstance() private val executableManager get() = GitExecutableManager.getInstance()
@@ -66,7 +72,16 @@ class GitCloneDialogComponent(project: Project,
val directoryName = Paths.get(getDirectory()).fileName.toString() val directoryName = Paths.get(getDirectory()).fileName.toString()
val parentDirectory = parent.toAbsolutePath().toString() val parentDirectory = parent.toAbsolutePath().toString()
GitCheckoutProvider.clone(project, Git.getInstance(), listener, destinationParent, sourceRepositoryURL, directoryName, parentDirectory) GitCheckoutProvider.clone(
project,
Git.getInstance(),
listener,
destinationParent,
sourceRepositoryURL,
directoryName,
parentDirectory,
(mainPanelCustomizer as GitCloneDialogMainPanelCustomizer).getShallowCloneOptions(),
)
val rememberedInputs = GitRememberedInputs.getInstance() val rememberedInputs = GitRememberedInputs.getInstance()
rememberedInputs.addUrl(sourceRepositoryURL) rememberedInputs.addUrl(sourceRepositoryURL)
rememberedInputs.cloneParentDir = parentDirectory rememberedInputs.cloneParentDir = parentDirectory
@@ -144,3 +159,35 @@ class GitCloneDialogComponent(project: Project,
FAILED FAILED
} }
} }
private class GitCloneDialogMainPanelCustomizer : DvcsCloneDialogComponent.MainPanelCustomizer() {
private var shallowClone = false
private var depth = 1
override fun configure(panel: Panel) {
if (Registry.`is`("git.clone.shallow")) {
with(panel) {
row {
var shallowCloneCheckbox = checkBox(GitBundle.message("clone.dialog.shallow.clone"))
.gap(RightGap.SMALL)
.bindSelected(::shallowClone)
val depthTextField = intTextField(1..Int.MAX_VALUE, 1)
.bindIntText(::depth)
.enabledIf(shallowCloneCheckbox.selected)
.gap(RightGap.SMALL)
depthTextField.component.toolTipText = GIT_CLONE_DEPTH_ARG
@Suppress("DialogTitleCapitalization")
label(GitBundle.message("clone.dialog.shallow.clone.depth"))
}.bottomGap(BottomGap.SMALL)
}
}
}
fun getShallowCloneOptions() = if (shallowClone) GitShallowCloneOptions(depth) else null
private companion object {
const val GIT_CLONE_DEPTH_ARG: @NlsSafe String = "--depth"
}
}

View File

@@ -6,11 +6,16 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.UriUtil import com.intellij.util.UriUtil
import com.intellij.util.io.URLUtil import com.intellij.util.io.URLUtil
import git4idea.checkout.GitCheckoutProvider import git4idea.checkout.GitCheckoutProvider
import git4idea.commands.Git
import git4idea.commands.GitCommand
import git4idea.commands.GitHttpAuthService import git4idea.commands.GitHttpAuthService
import git4idea.commands.GitHttpAuthenticator import git4idea.commands.GitHttpAuthenticator
import git4idea.commands.GitLineHandler
import git4idea.commands.GitShallowCloneOptions
import git4idea.config.GitVersion import git4idea.config.GitVersion
import git4idea.test.GitHttpAuthTestService import git4idea.test.GitHttpAuthTestService
import git4idea.test.GitPlatformTest import git4idea.test.GitPlatformTest
import git4idea.test.registerRepo
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -80,6 +85,18 @@ class GitRemoteTest : GitPlatformTest() {
assertCloneSuccessful(cloneWaiter) assertCloneSuccessful(cloneWaiter)
} }
fun `test shallow clone`() {
val cloneWaiter = cloneOnPooledThread(makeUrlWithUsername(), GitShallowCloneOptions(depth = 1))
assertPasswordAsked()
authenticator.supplyPassword(token)
assertCloneSuccessful(cloneWaiter)
val repo = registerRepo(project, testNioRoot)
assertTrue(repo.info.isShallow)
}
fun `test clone fails if incorrect password`() { fun `test clone fails if incorrect password`() {
val url = makeUrlWithUsername() val url = makeUrlWithUsername()
@@ -106,11 +123,11 @@ class GitRemoteTest : GitPlatformTest() {
assertErrorNotification("Clone failed", expectedAuthFailureMessage) assertErrorNotification("Clone failed", expectedAuthFailureMessage)
} }
private fun cloneOnPooledThread(url: String): CountDownLatch { private fun cloneOnPooledThread(url: String, shallowCloneOptions: GitShallowCloneOptions? = null): CountDownLatch {
val cloneWaiter = CountDownLatch(1) val cloneWaiter = CountDownLatch(1)
executeOnPooledThread { executeOnPooledThread {
val projectName = url.substring(url.lastIndexOf('/') + 1).replace(".git", "") val projectName = url.substring(url.lastIndexOf('/') + 1).replace(".git", "")
GitCheckoutProvider.doClone(project, git, projectName, testNioRoot.toString(), url) GitCheckoutProvider.doClone(project, git, projectName, testNioRoot.toString(), url, shallowCloneOptions)
cloneWaiter.countDown() cloneWaiter.countDown()
} }
return cloneWaiter return cloneWaiter

View File

@@ -0,0 +1,27 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package git4idea.repo
import git4idea.commands.Git
import git4idea.commands.GitShallowCloneOptions
import git4idea.test.GitSingleRepoTest
import git4idea.test.makeCommit
import git4idea.test.registerRepo
import kotlin.io.path.name
class GitShallowRepoTest: GitSingleRepoTest() {
fun `test shallow repo detection`() {
makeCommit("1.txt")
makeCommit("2.txt")
makeCommit("3.txt")
assertFalse(repo.info.isShallow)
val copy = projectNioRoot.resolve("copy")
val cloneResult = Git.getInstance().clone(project,
copy.parent.toFile(),
"file://${repo.root.path}", copy.name, GitShallowCloneOptions(1))
assertTrue(cloneResult.success())
val copyRepo = registerRepo(project, copy)
assertTrue(copyRepo.info.isShallow)
}
}