Compare commits

..

11 Commits

Author SHA1 Message Date
Abdelilah El Aissaoui
edcefd7a38 Changed version to 1.4.1 2024-11-03 13:23:32 +01:00
Abdelilah El Aissaoui
01b0827057 Fixed commit message disappear if pre-commit hook fails
Fixes #248
2024-11-02 23:53:07 +01:00
Abdelilah El Aissaoui
db1467c354
Having "cache" or "store" in credentials manager config does not longer throw an error 2024-10-31 01:37:49 +01:00
Abdelilah El Aissaoui
07e98dae10
Errors dialog can now be closed with ESC button 2024-10-31 01:37:22 +01:00
Abdelilah El Aissaoui
644f33ff2c
Fixed gitconfig not updating symlink origin instead of replacing symlink file
Fixes #245
2024-10-31 01:36:22 +01:00
Abdelilah El Aissaoui
3128341b93
Fixed crash when watch init has failed 2024-10-22 21:15:56 +02:00
Abdelilah El Aissaoui
1ef596ad3c
Updated latest.json for v1.4.0 2024-10-06 02:44:32 +02:00
Abdelilah El Aissaoui
4cc87a7289
Bumped version to 1.4.0 2024-10-01 20:35:09 +02:00
Abdelilah El Aissaoui
fcecd0380a
Merge pull request #239 from grizeldi/patch-1
Mention cargo-kotars in the development guide
2024-09-30 21:37:01 +02:00
grizeldi
fc2098781b Added automatic cargo-kotars install command to documentation 2024-09-28 20:21:05 +02:00
grizeldi
ce694ed44b
Mention cargo-kotars in the development guide 2024-09-27 23:32:20 +02:00
13 changed files with 90 additions and 87 deletions

View File

@ -8,6 +8,8 @@
environment,
please read [its documentation](https://www.rust-lang.org/). `cargo` and `rustc` must be available in the path in
order to build Gitnuro properly.
- **cargo-kotars:** A tool to run autogenerated bindings from Rust to Kotlin. You can install it using
`cargo install cargo-kotars --git https://github.com/JetpackDuba/kotars`
- **Perl:** Perl is required to build openssl (which is required for LibSSH to work).
- **Packages for Linux ARM64/aarch64**: You need to install the `aarch64-linux-gnu-gcc` package to cross compile the
Rust components to ARM from x86_64. You will also need to use `rustup` to add a new

View File

@ -12,17 +12,17 @@ plugins {
kotlin("jvm") version "2.0.0"
kotlin("plugin.serialization") version "2.0.20"
id("com.google.devtools.ksp") version "2.0.20-1.0.24"
id("org.jetbrains.compose") version "1.7.0-beta02"
id("org.jetbrains.compose") version "1.7.0"
id("org.jetbrains.kotlin.plugin.compose") version "2.0.20"
}
// Remember to update Constants.APP_VERSION when changing this version
val projectVersion = "1.4.0-rc01"
val projectVersion = "1.4.1"
val projectName = "Gitnuro"
// Required for JPackage, as it doesn't accept additional suffixes after the version.
val projectVersionSimplified = "1.4.0"
val projectVersionSimplified = "1.4.1"
val rustGeneratedSource = "${layout.buildDirectory.get()}/generated/source/uniffi/main/com/jetpackduba/gitnuro/java"

View File

@ -1,5 +1,5 @@
{
"appVersion": "1.3.1",
"appCode": 11,
"downloadUrl": "https://github.com/JetpackDuba/Gitnuro/releases/tag/v1.3.1"
"appVersion": "1.4.0",
"appCode": 14,
"downloadUrl": "https://github.com/JetpackDuba/Gitnuro/releases/tag/v1.4.0"
}

View File

@ -22,8 +22,8 @@ object AppConstants {
const val APP_NAME = "Gitnuro"
const val APP_DESCRIPTION =
"Gitnuro is a Git client that allows you to manage multiple repositories with a modern experience and live visual representation of your repositories' state."
const val APP_VERSION = "1.4.0-rc01"
const val APP_VERSION_CODE = 13
const val APP_VERSION = "1.4.1"
const val APP_VERSION_CODE = 15
const val VERSION_CHECK_URL = "https://raw.githubusercontent.com/JetpackDuba/Gitnuro/main/latest.json"
}

View File

@ -2,6 +2,7 @@ package com.jetpackduba.gitnuro.credentials
import com.jetpackduba.gitnuro.exceptions.NotSupportedHelper
import com.jetpackduba.gitnuro.git.remote_operations.CredentialsCache
import com.jetpackduba.gitnuro.logging.printError
import com.jetpackduba.gitnuro.logging.printLog
import com.jetpackduba.gitnuro.managers.IShellManager
import com.jetpackduba.gitnuro.repositories.AppSettingsRepository
@ -259,7 +260,8 @@ class HttpCredentialsProvider @AssistedInject constructor(
val credentialHelperPath = uriSpecificCredentialHelper ?: genericCredentialHelper ?: return null
if (credentialHelperPath == "cache" || credentialHelperPath == "store") {
throw NotSupportedHelper("Invalid credentials helper: \"$credentialHelperPath\" is not yet supported")
printError(TAG, "Invalid credentials helper: \"$credentialHelperPath\" is not yet supported")
return null
}
// TODO Try to use "git-credential-manager-core" when "manager-core" is detected. Works for linux but requires testing for mac/windows

View File

@ -1,11 +1,6 @@
package com.jetpackduba.gitnuro.exceptions
class WatcherInitException(
code: Int,
message: String = codeToMessage(code),
) : GitnuroException(message)
private fun codeToMessage(code: Int): String {
fun codeToMessage(code: Int): String {
return when (code) {
1 /*is WatcherInitException.Generic*/, 2 /*is WatcherInitException.Io*/ -> "Could not watch directory. Check if it exists and you have read permissions."
3 /*is WatcherInitException.PathNotFound*/ -> "Path not found, check if your repository still exists"

View File

@ -3,7 +3,6 @@ package com.jetpackduba.gitnuro.git
import FileWatcher
import WatchDirectoryNotifier
import com.jetpackduba.gitnuro.di.TabScope
import com.jetpackduba.gitnuro.exceptions.WatcherInitException
import com.jetpackduba.gitnuro.git.workspace.GetIgnoreRulesUseCase
import com.jetpackduba.gitnuro.system.systemSeparator
import kotlinx.coroutines.CoroutineScope
@ -25,8 +24,8 @@ class FileChangesWatcher @Inject constructor(
private val getIgnoreRulesUseCase: GetIgnoreRulesUseCase,
private val tabScope: CoroutineScope,
) : AutoCloseable {
private val _changesNotifier = MutableSharedFlow<Boolean>()
val changesNotifier: SharedFlow<Boolean> = _changesNotifier
private val _changesNotifier = MutableSharedFlow<WatcherEvent>()
val changesNotifier: SharedFlow<WatcherEvent> = _changesNotifier
private val fileWatcher = FileWatcher.new()
private var shouldKeepLooping = true
@ -71,14 +70,16 @@ class FileChangesWatcher @Inject constructor(
if (!areAllPathsIgnored) {
println("Emitting changes $hasGitIgnoreChanged")
_changesNotifier.emit(hasGitDirChanged)
_changesNotifier.emit(WatcherEvent.RepositoryChanged(hasGitDirChanged))
}
}
}
override fun onError(code: Int) {
throw WatcherInitException(code)
tabScope.launch {
_changesNotifier.emit(WatcherEvent.WatchInitError(code))
}
}
}
@ -90,4 +91,9 @@ class FileChangesWatcher @Inject constructor(
fileWatcher.close()
}
}
sealed interface WatcherEvent {
data class RepositoryChanged(val hasGitDirChanged: Boolean) : WatcherEvent
data class WatchInitError(val code: Int) : WatcherEvent
}

View File

@ -15,9 +15,12 @@ class SaveAuthorUseCase @Inject constructor() {
repoConfig.load()
if (globalConfig is FileBasedConfig) {
globalConfig.setStringProperty("user", null, "name", newAuthorInfo.globalName)
globalConfig.setStringProperty("user", null, "email", newAuthorInfo.globalEmail)
globalConfig.save()
val canonicalConfigFile = globalConfig.file.canonicalFile
val globalRepoConfig = FileBasedConfig(canonicalConfigFile, git.repository.fs)
globalRepoConfig.setStringProperty("user", null, "name", newAuthorInfo.globalName)
globalRepoConfig.setStringProperty("user", null, "email", newAuthorInfo.globalEmail)
globalRepoConfig.save()
}
config.setStringProperty("user", null, "name", newAuthorInfo.name)

View File

@ -89,7 +89,6 @@ fun UncommittedChanges(
val doCommit = {
statusViewModel.commit(commitMessage)
onStagedDiffEntrySelected(null)
setCommitMessage("")
}
val canCommit = commitMessage.isNotEmpty() && stageStateUi.hasStagedFiles

View File

@ -32,7 +32,9 @@ fun ErrorDialog(
val clipboard = LocalClipboardManager.current
var showStackTrace by remember { mutableStateOf(false) }
MaterialDialog {
MaterialDialog (
onCloseRequested = onAccept,
) {
Column(
modifier = Modifier
.width(580.dp)

View File

@ -2,7 +2,7 @@ package com.jetpackduba.gitnuro.viewmodels
import com.jetpackduba.gitnuro.SharedRepositoryStateManager
import com.jetpackduba.gitnuro.TaskType
import com.jetpackduba.gitnuro.exceptions.WatcherInitException
import com.jetpackduba.gitnuro.exceptions.codeToMessage
import com.jetpackduba.gitnuro.git.*
import com.jetpackduba.gitnuro.git.branches.CreateBranchUseCase
import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
@ -110,6 +110,8 @@ class RepositoryOpenViewModel @Inject constructor(
var authorViewModel: AuthorViewModel? = null
private set
private var hasGitDirChanged = false
init {
tabScope.run {
launch {
@ -119,7 +121,7 @@ class RepositoryOpenViewModel @Inject constructor(
}
launch {
watchRepositoryChanges(tabState.git)
watchRepositoryChanges()
}
}
}
@ -158,55 +160,53 @@ class RepositoryOpenViewModel @Inject constructor(
* the app by constantly running "git status" or even full refreshes.
*
*/
private suspend fun watchRepositoryChanges(git: Git) = tabScope.launch(Dispatchers.IO) {
var hasGitDirChanged = false
private suspend fun watchRepositoryChanges() = tabScope.launch(Dispatchers.IO) {
launch {
fileChangesWatcher.changesNotifier.collect { latestUpdateChangedGitDir ->
val isOperationRunning = tabState.operationRunning
fileChangesWatcher.changesNotifier.collect { watcherEvent ->
when (watcherEvent) {
is WatcherEvent.RepositoryChanged -> repositoryChanged(watcherEvent.hasGitDirChanged)
is WatcherEvent.WatchInitError -> {
val message = codeToMessage(watcherEvent.code)
errorsManager.addError(
newErrorNow(
exception = Exception(message),
taskType = TaskType.CHANGES_DETECTION,
),
)
if (!isOperationRunning) { // Only update if there isn't any process running
printDebug(TAG, "Detected changes in the repository's directory")
val currentTimeMillis = System.currentTimeMillis()
if (
latestUpdateChangedGitDir &&
currentTimeMillis - tabState.lastOperation < MIN_TIME_AFTER_GIT_OPERATION
) {
printDebug(TAG, "Git operation was executed recently, ignoring file system change")
return@collect
}
if (latestUpdateChangedGitDir) {
hasGitDirChanged = true
}
if (isActive) {
updateApp(hasGitDirChanged)
}
hasGitDirChanged = false
} else {
printDebug(TAG, "Ignored file events during operation")
}
}
}
}
try {
fileChangesWatcher.watchDirectoryPath(
repository = git.repository,
)
} catch (ex: WatcherInitException) {
val message = ex.message
if (message != null) {
errorsManager.addError(
newErrorNow(
exception = ex,
taskType = TaskType.CHANGES_DETECTION,
),
)
private suspend fun CoroutineScope.repositoryChanged(hasGitDirChanged: Boolean) {
val isOperationRunning = tabState.operationRunning
if (!isOperationRunning) { // Only update if there isn't any process running
printDebug(TAG, "Detected changes in the repository's directory")
val currentTimeMillis = System.currentTimeMillis()
if (
hasGitDirChanged &&
currentTimeMillis - tabState.lastOperation < MIN_TIME_AFTER_GIT_OPERATION
) {
printDebug(TAG, "Git operation was executed recently, ignoring file system change")
return
}
if (hasGitDirChanged) {
this@RepositoryOpenViewModel.hasGitDirChanged = true
}
if (isActive) {
updateApp(hasGitDirChanged)
}
this@RepositoryOpenViewModel.hasGitDirChanged = false
} else {
printDebug(TAG, "Ignored file events during operation")
}
}

View File

@ -395,7 +395,9 @@ class StatusViewModel @Inject constructor(
val personIdent = getPersonIdent(git)
doCommitUseCase(git, commitMessage, amend, personIdent)
updateCommitMessage("")
_commitMessageChangesFlow.emit("")
_isAmend.value = false
positiveNotification(if (isAmend.value) "Commit amended" else "New commit created")

View File

@ -1,45 +1,37 @@
package com.jetpackduba.gitnuro.viewmodels
import com.jetpackduba.gitnuro.SharedRepositoryStateManager
import com.jetpackduba.gitnuro.TaskType
import com.jetpackduba.gitnuro.credentials.CredentialsAccepted
import com.jetpackduba.gitnuro.credentials.CredentialsState
import com.jetpackduba.gitnuro.credentials.CredentialsStateManager
import com.jetpackduba.gitnuro.exceptions.WatcherInitException
import com.jetpackduba.gitnuro.git.*
import com.jetpackduba.gitnuro.git.branches.CreateBranchUseCase
import com.jetpackduba.gitnuro.git.rebase.RebaseInteractiveState
import com.jetpackduba.gitnuro.git.FileChangesWatcher
import com.jetpackduba.gitnuro.git.ProcessingState
import com.jetpackduba.gitnuro.git.RefreshType
import com.jetpackduba.gitnuro.git.TabState
import com.jetpackduba.gitnuro.git.repository.InitLocalRepositoryUseCase
import com.jetpackduba.gitnuro.git.repository.OpenRepositoryUseCase
import com.jetpackduba.gitnuro.git.repository.OpenSubmoduleRepositoryUseCase
import com.jetpackduba.gitnuro.git.stash.StashChangesUseCase
import com.jetpackduba.gitnuro.git.workspace.StageUntrackedFileUseCase
import com.jetpackduba.gitnuro.logging.printDebug
import com.jetpackduba.gitnuro.logging.printLog
import com.jetpackduba.gitnuro.managers.AppStateManager
import com.jetpackduba.gitnuro.managers.ErrorsManager
import com.jetpackduba.gitnuro.managers.newErrorNow
import com.jetpackduba.gitnuro.models.AuthorInfoSimple
import com.jetpackduba.gitnuro.models.errorNotification
import com.jetpackduba.gitnuro.models.positiveNotification
import com.jetpackduba.gitnuro.system.OpenFilePickerUseCase
import com.jetpackduba.gitnuro.system.OpenUrlInBrowserUseCase
import com.jetpackduba.gitnuro.system.PickerType
import com.jetpackduba.gitnuro.ui.IVerticalSplitPaneConfig
import com.jetpackduba.gitnuro.ui.SelectedItem
import com.jetpackduba.gitnuro.ui.TabsManager
import com.jetpackduba.gitnuro.ui.VerticalSplitPaneConfig
import com.jetpackduba.gitnuro.updates.Update
import com.jetpackduba.gitnuro.updates.UpdatesRepository
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.errors.CheckoutConflictException
import org.eclipse.jgit.blame.BlameResult
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.lib.RepositoryState
import org.eclipse.jgit.revwalk.RevCommit
import java.awt.Desktop
import java.io.File
import javax.inject.Inject
import javax.inject.Provider