Refactored repository watcher
Refactored to use a rust implementation instead of the java impl, because the java impl has been unrelible in linux and macos
This commit is contained in:
parent
70597aa16a
commit
7a7eb3ad93
2
.gitignore
vendored
2
.gitignore
vendored
@ -13,3 +13,5 @@ build/
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
rs/target/
|
||||
rs/Cargo.lock
|
||||
|
@ -16,9 +16,17 @@ plugins {
|
||||
val projectVersion = "1.2.1"
|
||||
val projectName = "Gitnuro"
|
||||
|
||||
val rustGeneratedSource = "${buildDir}/generated/source/uniffi/main/com/jetpackduba/gitnuro/java"
|
||||
|
||||
group = "com.jetpackduba"
|
||||
version = projectVersion
|
||||
|
||||
sourceSets.getByName("main") {
|
||||
kotlin.sourceSets.main.get().kotlin.srcDir(rustGeneratedSource)
|
||||
}
|
||||
|
||||
sourceSets.main.get().java.srcDirs("src/main/resources").includes.addAll(arrayOf("**/*.*"))
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
@ -53,7 +61,7 @@ dependencies {
|
||||
implementation("io.github.oshai:kotlin-logging-jvm:4.0.0-beta-27")
|
||||
implementation("org.slf4j:slf4j-api:2.0.7")
|
||||
implementation("org.slf4j:slf4j-reload4j:2.0.7")
|
||||
|
||||
implementation("io.arrow-kt:arrow-core:1.2.0")
|
||||
}
|
||||
|
||||
fun currentOs(): OS {
|
||||
@ -101,6 +109,14 @@ compose.desktop {
|
||||
application {
|
||||
mainClass = "com.jetpackduba.gitnuro.MainKt"
|
||||
|
||||
this@application.dependsOn("rust_generateKotlinFromUdl")
|
||||
this@application.dependsOn("rust_build")
|
||||
this@application.dependsOn("rust_copyBuild")
|
||||
|
||||
sourceSets.forEach {
|
||||
it.java.srcDir(rustGeneratedSource)
|
||||
}
|
||||
|
||||
nativeDistributions {
|
||||
includeAllModules = true
|
||||
packageName = projectName
|
||||
@ -143,3 +159,58 @@ task("fatJarLinux", type = Jar::class) {
|
||||
}
|
||||
with(tasks.jar.get() as CopySpec)
|
||||
}
|
||||
|
||||
|
||||
task("rust_generateKotlinFromUdl", type = Exec::class) {
|
||||
println("Generate Kotlin")
|
||||
workingDir = File(project.projectDir, "rs")
|
||||
commandLine = listOf(
|
||||
"cargo", "run", "--features=uniffi/cli",
|
||||
"--bin", "uniffi-bindgen", "generate", "src/repository_watcher.udl",
|
||||
"--language", "kotlin",
|
||||
"--out-dir", rustGeneratedSource
|
||||
)
|
||||
}
|
||||
|
||||
task("rust_build", type = Exec::class) {
|
||||
println("Build rs called")
|
||||
workingDir = File(project.projectDir, "rs")
|
||||
commandLine = listOf(
|
||||
"cargo", "build", "--release", "--features=uniffi/cli",
|
||||
)
|
||||
}
|
||||
|
||||
tasks.getByName("compileKotlin").dependsOn("rustTasks")
|
||||
tasks.getByName("compileTestKotlin").dependsOn("rustTasks")
|
||||
|
||||
|
||||
task("tasksList") {
|
||||
println("Tasks")
|
||||
tasks.forEach {
|
||||
println("- ${it.name}")
|
||||
}
|
||||
}
|
||||
|
||||
task("rustTasks") {
|
||||
dependsOn("rust_build")
|
||||
dependsOn("rust_generateKotlinFromUdl")
|
||||
dependsOn("rust_copyBuild")
|
||||
}
|
||||
|
||||
task("rust_copyBuild", type = Exec::class) {
|
||||
val outputDir = "${buildDir}/classes/kotlin/main"
|
||||
println("Copy rs called")
|
||||
workingDir = File(project.projectDir, "rs")
|
||||
|
||||
val f = File(outputDir)
|
||||
f.mkdirs()
|
||||
|
||||
val lib = when (currentOs()) {
|
||||
OS.LINUX -> "libuniffi_gitnuro.so"
|
||||
OS.WINDOWS -> "libuniffi_gitnuro.dll"
|
||||
OS.MAC -> "libuniffi_gitnuro.so" //TODO or is it a dylib? must be tested
|
||||
}
|
||||
commandLine = listOf(
|
||||
"cp", "target/release/libgitnuro_rs.so", "$outputDir/$lib",
|
||||
)
|
||||
}
|
||||
|
23
rs/Cargo.toml
Normal file
23
rs/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "gitnuro-rs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
name = "gitnuro_rs"
|
||||
|
||||
[dependencies]
|
||||
uniffi = { version = "0.24.1" }
|
||||
notify = "6.0.1"
|
||||
thiserror = "1.0.43"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.24.1", features = [ "build" ] }
|
||||
|
||||
[[bin]]
|
||||
# This can be whatever name makes sense for your project, but the rest of this tutorial assumes uniffi-bindgen.
|
||||
name = "uniffi-bindgen"
|
||||
path = "uniffi-bindgen.rs"
|
3
rs/build.rs
Normal file
3
rs/build.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
uniffi::generate_scaffolding("src/repository_watcher.udl").unwrap();
|
||||
}
|
118
rs/src/lib.rs
Normal file
118
rs/src/lib.rs
Normal file
@ -0,0 +1,118 @@
|
||||
extern crate notify;
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc::{channel, RecvTimeoutError};
|
||||
use std::time::Duration;
|
||||
|
||||
use notify::{Config, Error, ErrorKind, Event, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
|
||||
uniffi::include_scaffolding!("repository_watcher");
|
||||
|
||||
fn watch_directory(
|
||||
path: String,
|
||||
notifier: Box<dyn WatchDirectoryNotifier>,
|
||||
) -> Result<(), WatcherInitError> {
|
||||
// Create a channel to receive the events.
|
||||
let (tx, rx) = channel();
|
||||
|
||||
// Create a watcher object, delivering debounced events.
|
||||
// The notification back-end is selected based on the platform.
|
||||
let config = Config::default();
|
||||
config.with_poll_interval(Duration::from_secs(3600));
|
||||
|
||||
let mut watcher =
|
||||
RecommendedWatcher::new(tx, config).map_err(|err| err.kind.into_watcher_init_error())?;
|
||||
|
||||
// Add a path to be watched. All files and directories at that path and
|
||||
// below will be monitored for changes.
|
||||
watcher
|
||||
.watch(Path::new(path.as_str()), RecursiveMode::Recursive)
|
||||
.map_err(|err| err.kind.into_watcher_init_error())?;
|
||||
|
||||
while notifier.should_keep_looping() {
|
||||
match rx.recv_timeout(Duration::from_secs(1)) {
|
||||
Ok(e) => {
|
||||
println!("{:?}", e);
|
||||
|
||||
if let Some(paths) = get_paths_from_event_result(&e) {
|
||||
notifier.detected_change(paths)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if e != RecvTimeoutError::Timeout {
|
||||
println!("Watch error: {:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watcher
|
||||
.unwatch(Path::new(path.as_str()))
|
||||
.map_err(|err| err.kind.into_watcher_init_error())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_paths_from_event_result(event_result: &Result<Event, Error>) -> Option<Vec<String>> {
|
||||
match event_result {
|
||||
Ok(event) => {
|
||||
let events: Vec<String> = event
|
||||
.paths
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter_map(|path| path.into_os_string().into_string().ok())
|
||||
.collect();
|
||||
|
||||
if events.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(events)
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{:?}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WatchDirectoryNotifier: Send + Sync + Debug {
|
||||
fn should_keep_looping(&self) -> bool;
|
||||
fn detected_change(&self, paths: Vec<String>);
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum WatcherInitError {
|
||||
#[error("{error}")]
|
||||
Generic { error: String },
|
||||
#[error("IO Error")]
|
||||
Io { error: String },
|
||||
#[error("Path not found")]
|
||||
PathNotFound,
|
||||
#[error("Can not remove watch, it has not been found")]
|
||||
WatchNotFound,
|
||||
#[error("Invalid configuration")]
|
||||
InvalidConfig,
|
||||
#[error("Max files reached. Check the inotify limit")]
|
||||
MaxFilesWatch,
|
||||
}
|
||||
|
||||
trait WatcherInitErrorConverter {
|
||||
fn into_watcher_init_error(self) -> WatcherInitError;
|
||||
}
|
||||
|
||||
impl WatcherInitErrorConverter for ErrorKind {
|
||||
fn into_watcher_init_error(self) -> WatcherInitError {
|
||||
match self {
|
||||
ErrorKind::Generic(err) => WatcherInitError::Generic { error: err },
|
||||
ErrorKind::Io(err) => WatcherInitError::Generic {
|
||||
error: err.to_string(),
|
||||
},
|
||||
ErrorKind::PathNotFound => WatcherInitError::PathNotFound,
|
||||
ErrorKind::WatchNotFound => WatcherInitError::WatchNotFound,
|
||||
ErrorKind::InvalidConfig(_) => WatcherInitError::InvalidConfig,
|
||||
ErrorKind::MaxFilesWatch => WatcherInitError::MaxFilesWatch,
|
||||
}
|
||||
}
|
||||
}
|
20
rs/src/repository_watcher.udl
Normal file
20
rs/src/repository_watcher.udl
Normal file
@ -0,0 +1,20 @@
|
||||
namespace gitnuro {
|
||||
[Throws=WatcherInitError]
|
||||
void watch_directory(string path, WatchDirectoryNotifier checker);
|
||||
};
|
||||
|
||||
callback interface WatchDirectoryNotifier {
|
||||
boolean should_keep_looping();
|
||||
|
||||
void detected_change(sequence<string> paths);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface WatcherInitError {
|
||||
Generic(string error);
|
||||
Io(string error);
|
||||
PathNotFound();
|
||||
WatchNotFound();
|
||||
InvalidConfig();
|
||||
MaxFilesWatch();
|
||||
};
|
3
rs/uniffi-bindgen.rs
Normal file
3
rs/uniffi-bindgen.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
uniffi::uniffi_bindgen_main()
|
||||
}
|
@ -1,89 +1,59 @@
|
||||
package com.jetpackduba.gitnuro.git
|
||||
|
||||
import com.jetpackduba.gitnuro.git.workspace.GetIgnoreRulesUseCase
|
||||
import com.jetpackduba.gitnuro.system.systemSeparator
|
||||
import com.jetpackduba.gitnuro.logging.printLog
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.*
|
||||
import java.nio.file.StandardWatchEventKinds.*
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import org.eclipse.jgit.lib.Repository
|
||||
import uniffi.gitnuro.WatchDirectoryNotifier
|
||||
import uniffi.gitnuro.watchDirectory
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val TAG = "FileChangesWatcher"
|
||||
|
||||
class FileChangesWatcher @Inject constructor() {
|
||||
|
||||
class FileChangesWatcher @Inject constructor(
|
||||
private val getIgnoreRulesUseCase: GetIgnoreRulesUseCase,
|
||||
) {
|
||||
private val _changesNotifier = MutableSharedFlow<Boolean>()
|
||||
val changesNotifier: SharedFlow<Boolean> = _changesNotifier
|
||||
val keys = mutableMapOf<WatchKey, Path>()
|
||||
|
||||
suspend fun watchDirectoryPath(pathStr: String, ignoredDirsPath: List<String>) = withContext(Dispatchers.IO) {
|
||||
val watchService = FileSystems.getDefault().newWatchService()
|
||||
|
||||
val path = Paths.get(pathStr)
|
||||
|
||||
path.register(
|
||||
watchService,
|
||||
ENTRY_CREATE,
|
||||
ENTRY_DELETE,
|
||||
ENTRY_MODIFY
|
||||
)
|
||||
|
||||
// register directory and subdirectories but ignore dirs by gitignore
|
||||
Files.walkFileTree(path, object : SimpleFileVisitor<Path>() {
|
||||
@Throws(IOException::class)
|
||||
override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
val isIgnoredDirectory = ignoredDirsPath.any { "$pathStr/$it" == dir.toString() }
|
||||
|
||||
return if (!isIgnoredDirectory && !isGitDir(dir, pathStr)) {
|
||||
val watchKey = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
|
||||
keys[watchKey] = dir
|
||||
FileVisitResult.CONTINUE
|
||||
} else {
|
||||
FileVisitResult.SKIP_SUBTREE
|
||||
}
|
||||
suspend fun watchDirectoryPath(
|
||||
repository: Repository,
|
||||
pathStr: String
|
||||
) = withContext(Dispatchers.IO) {
|
||||
var ignoreRules = getIgnoreRulesUseCase(repository)
|
||||
val checker = object : WatchDirectoryNotifier {
|
||||
override fun shouldKeepLooping(): Boolean {
|
||||
return isActive
|
||||
}
|
||||
})
|
||||
|
||||
var key: WatchKey
|
||||
while (watchService.take().also { key = it } != null) {
|
||||
val events = key.pollEvents()
|
||||
override fun detectedChange(paths: List<String>) = runBlocking {
|
||||
val hasGitIgnoreChanged = paths.any { it == "$pathStr$systemSeparator.gitignore" }
|
||||
|
||||
val dir = keys[key] ?: return@withContext
|
||||
if (hasGitIgnoreChanged) {
|
||||
ignoreRules = getIgnoreRulesUseCase(repository)
|
||||
}
|
||||
|
||||
_changesNotifier.emit(false)
|
||||
|
||||
// Check if new directories have been added to add them to the watchService
|
||||
launch(Dispatchers.IO) {
|
||||
for (event in events) {
|
||||
if (event.kind() == ENTRY_CREATE) {
|
||||
try {
|
||||
val eventFile = File(dir.toAbsolutePath().toString() + systemSeparator + event.context())
|
||||
|
||||
if (eventFile.isDirectory) {
|
||||
val eventPath = eventFile.toPath()
|
||||
printLog(TAG, "New directory $eventFile detected, adding it to watchService")
|
||||
val watchKey =
|
||||
eventPath.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
|
||||
keys[watchKey] = eventPath
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
}
|
||||
val areAllPathsIgnored = paths.all { path ->
|
||||
ignoreRules.any { rule ->
|
||||
rule.isMatch(path, Files.isDirectory(Paths.get(path)))
|
||||
}
|
||||
}
|
||||
|
||||
val hasGitDirChanged = paths.any { it == "$pathStr$systemSeparator.git" }
|
||||
|
||||
if (!areAllPathsIgnored) {
|
||||
_changesNotifier.emit(hasGitDirChanged)
|
||||
}
|
||||
}
|
||||
|
||||
key.reset()
|
||||
}
|
||||
}
|
||||
|
||||
private fun isGitDir(dir: Path, pathStr: String): Boolean {
|
||||
return dir.startsWith("$pathStr$systemSeparator.git$systemSeparator")
|
||||
watchDirectory(pathStr, checker)
|
||||
}
|
||||
}
|
@ -142,7 +142,7 @@ class TabState @Inject constructor(
|
||||
val containsCancellation = exceptionContainsCancellation(ex)
|
||||
|
||||
if (showError && !containsCancellation)
|
||||
errorsManager.addError(newErrorNow(ex, ex.message.orEmpty()))
|
||||
errorsManager.addError(newErrorNow(ex, null, ex.message.orEmpty()))
|
||||
|
||||
printError(TAG, ex.message.orEmpty(), ex)
|
||||
} finally {
|
||||
@ -190,7 +190,7 @@ class TabState @Inject constructor(
|
||||
val containsCancellation = exceptionContainsCancellation(ex)
|
||||
|
||||
if (showError && !containsCancellation)
|
||||
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
||||
errorsManager.addError(newErrorNow(ex, null, ex.localizedMessage))
|
||||
|
||||
printError(TAG, ex.message.orEmpty(), ex)
|
||||
} finally {
|
||||
@ -221,7 +221,7 @@ class TabState @Inject constructor(
|
||||
hasProcessFailed = true
|
||||
|
||||
if (showError)
|
||||
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
||||
errorsManager.addError(newErrorNow(ex, null, ex.localizedMessage))
|
||||
|
||||
printError(TAG, ex.message.orEmpty(), ex)
|
||||
} finally {
|
||||
@ -284,7 +284,7 @@ class TabState @Inject constructor(
|
||||
callback(it)
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
||||
errorsManager.addError(newErrorNow(ex, null, ex.localizedMessage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
package com.jetpackduba.gitnuro.git.workspace
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.eclipse.jgit.ignore.FastIgnoreRule
|
||||
import org.eclipse.jgit.lib.Config
|
||||
import org.eclipse.jgit.lib.Repository
|
||||
import java.io.File
|
||||
import java.nio.file.FileSystems
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class GetIgnoreRulesUseCase @Inject constructor() {
|
||||
suspend operator fun invoke(repository: Repository): List<FastIgnoreRule> = withContext(Dispatchers.IO) {
|
||||
val ignoreLines = mutableListOf<String>()
|
||||
|
||||
val repositoryExcludeFile = File(repository.directory, ".git/info/exclude")
|
||||
val ignoreFile = File(repository.workTree, ".gitignore")
|
||||
|
||||
repository.config.load()
|
||||
val baseConfig: Config? = repository.config.baseConfig
|
||||
|
||||
if (repositoryExcludeFile.exists() && repositoryExcludeFile.isFile) {
|
||||
ignoreLines.addAll(repositoryExcludeFile.readLines())
|
||||
}
|
||||
|
||||
if (ignoreFile.exists() && ignoreFile.isFile) {
|
||||
ignoreLines.addAll(ignoreFile.readLines())
|
||||
}
|
||||
|
||||
if (baseConfig != null) {
|
||||
var excludesFilePath = baseConfig.getString("core", null, "excludesFile")
|
||||
|
||||
if (excludesFilePath.startsWith("~")) {
|
||||
excludesFilePath = excludesFilePath.replace("~", System.getProperty("user.home").orEmpty())
|
||||
}
|
||||
|
||||
val excludesFile = FileSystems
|
||||
.getDefault()
|
||||
.getPath(excludesFilePath)
|
||||
.normalize()
|
||||
.toFile()
|
||||
|
||||
if (excludesFile.exists() && excludesFile.isFile) {
|
||||
ignoreLines.addAll(excludesFile.readLines())
|
||||
}
|
||||
}
|
||||
|
||||
ignoreLines.map { FastIgnoreRule(it) }
|
||||
}
|
||||
}
|
@ -34,6 +34,22 @@ class ErrorsManager @Inject constructor() {
|
||||
}
|
||||
|
||||
|
||||
data class Error(val date: Long, val exception: Exception, val message: String)
|
||||
data class Error(
|
||||
val date: Long,
|
||||
val exception: Exception,
|
||||
val title: String?,
|
||||
val message: String
|
||||
)
|
||||
|
||||
fun newErrorNow(exception: Exception, message: String) = Error(System.currentTimeMillis(), exception, message)
|
||||
fun newErrorNow(
|
||||
exception: Exception,
|
||||
title: String?,
|
||||
message: String,
|
||||
): Error {
|
||||
return Error(
|
||||
date = System.currentTimeMillis(),
|
||||
exception = exception,
|
||||
title = title,
|
||||
message = message
|
||||
)
|
||||
}
|
@ -27,7 +27,7 @@ fun ErrorDialog(
|
||||
) {
|
||||
Row {
|
||||
Text(
|
||||
text = "Error",
|
||||
text = error.title ?: "Error",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
|
@ -69,11 +69,10 @@ class SettingsViewModel @Inject constructor(
|
||||
null
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
newErrorNow(ex, "Failed to parse selected theme JSON. Please check if it's valid and try again.")
|
||||
newErrorNow(ex, "Saving theme failed", "Failed to parse selected theme JSON. Please check if it's valid and try again.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun resetInfo() {
|
||||
commitsLimit = appSettings.commitsLimit
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ 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 uniffi.gitnuro.WatcherInitException
|
||||
import java.awt.Desktop
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
@ -181,7 +182,7 @@ class TabViewModel @Inject constructor(
|
||||
} catch (ex: Exception) {
|
||||
onRepositoryChanged(null)
|
||||
ex.printStackTrace()
|
||||
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
||||
errorsManager.addError(newErrorNow(ex, null, ex.localizedMessage))
|
||||
_repositorySelectionStatus.value = RepositorySelectionStatus.None
|
||||
}
|
||||
}
|
||||
@ -207,7 +208,6 @@ class TabViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private suspend fun watchRepositoryChanges(git: Git) = tabScope.launch(Dispatchers.IO) {
|
||||
val ignored = git.status().call().ignoredNotInIndex.toList()
|
||||
var asyncJob: Job? = null
|
||||
var lastNotify = 0L
|
||||
var hasGitDirChanged = false
|
||||
@ -249,10 +249,32 @@ class TabViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
fileChangesWatcher.watchDirectoryPath(
|
||||
pathStr = git.repository.workTree.absolutePath,
|
||||
ignoredDirsPath = ignored,
|
||||
)
|
||||
|
||||
try {
|
||||
fileChangesWatcher.watchDirectoryPath(
|
||||
repository = git.repository,
|
||||
pathStr = git.repository.workTree.absolutePath,
|
||||
)
|
||||
} catch (ex: WatcherInitException) {
|
||||
val message = when (ex) {
|
||||
is WatcherInitException.Generic -> ex.error
|
||||
is WatcherInitException.InvalidConfig -> "Invalid configuration"
|
||||
is WatcherInitException.Io -> ex.error
|
||||
is WatcherInitException.MaxFilesWatch -> "Reached the limit of files that can be watched. Please increase the system inotify limit to be able to detect the changes on this repository."
|
||||
is WatcherInitException.PathNotFound -> "Path not found, check if your repository still exists"
|
||||
is WatcherInitException.WatchNotFound -> null // This should never trigger as we don't unwatch files
|
||||
}
|
||||
|
||||
if(message != null) {
|
||||
errorsManager.addError(
|
||||
newErrorNow(
|
||||
exception = ex,
|
||||
title = "Repository changes detection has stopped working",
|
||||
message = message,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateApp(hasGitDirChanged: Boolean) {
|
||||
|
Loading…
Reference in New Issue
Block a user