mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-04 17:20:55 +07:00
[git] IJPL-84816 Support shallow clone at "Get from Version Control" screen
GitOrigin-RevId: 27e27e722ce57d5807e7081bedbaf4f487077962
This commit is contained in:
committed by
intellij-monorepo-bot
parent
13a6cb55f8
commit
ca7bfcfe06
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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?)
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
27
plugins/git4idea/tests/git4idea/repo/GitShallowRepoTest.kt
Normal file
27
plugins/git4idea/tests/git4idea/repo/GitShallowRepoTest.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user