From 094252cbb36bbc79fba808a8990b33b258dfe04c Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Wed, 23 Oct 2024 20:17:52 +0200
Subject: [PATCH 01/84] l'ajout de la classe BackUpManagerJGit
---
build.gradle | 2 +-
.../autosaveandbackup/BackUpManagerJGit.java | 157 ++++++++++++++++++
.../gui/autosaveandbackup/BackupManager.java | 19 ++-
3 files changed, 172 insertions(+), 6 deletions(-)
create mode 100644 src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
diff --git a/build.gradle b/build.gradle
index 0c7558981c8..e6d69d6976e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -153,7 +153,7 @@ jacoco {
dependencies {
// Include all jar-files in the 'lib' folder as dependencies
implementation fileTree(dir: 'lib', includes: ['*.jar'])
-
+ implementation 'org.eclipse.jgit:org.eclipse.jgit:6.7.0.202309050840-r'
def pdfbox = "3.0.3"
implementation ("org.apache.pdfbox:pdfbox:$pdfbox") {
exclude group: 'commons-logging'
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
new file mode 100644
index 00000000000..13cf7b03628
--- /dev/null
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
@@ -0,0 +1,157 @@
+package org.jabref.gui.autosaveandbackup;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.FileTime;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+
+import org.jabref.gui.LibraryTab;
+import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.logic.util.CoarseChangeFilter;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntryTypesManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BackUpManagerJGit {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackupManager.class);
+
+
+ private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
+
+ private static Set runningInstances = new HashSet<>();
+
+ private final BibDatabaseContext bibDatabaseContext;
+ private final CliPreferences preferences;
+ private final ScheduledThreadPoolExecutor executor;
+ private final CoarseChangeFilter changeFilter;
+ private final BibEntryTypesManager entryTypesManager;
+ private final LibraryTab libraryTab;
+
+ // Contains a list of all backup paths
+ // During writing, the less recent backup file is deleted
+ //private final Queue backupFilesQueue = new LinkedBlockingQueue<>();
+ private boolean needsBackup = false;
+
+ public BackUpManagerJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
+ this.bibDatabaseContext = bibDatabaseContext;
+ this.entryTypesManager = entryTypesManager;
+ this.preferences = preferences;
+ this.executor = new ScheduledThreadPoolExecutor(2);
+ this.libraryTab = libraryTab;
+
+ changeFilter = new CoarseChangeFilter(bibDatabaseContext);
+ changeFilter.registerListener(this);
+ }
+ /**
+ * Starts the BackupManager which is associated with the given {@link BibDatabaseContext}. As long as no database
+ * file is present in {@link BibDatabaseContext}, the {@link BackupManager} will do nothing.
+ *
+ * This method is not thread-safe. The caller has to ensure that this method is not called in parallel.
+ *
+ * @param bibDatabaseContext Associated {@link BibDatabaseContext}
+ */
+
+ public static BackUpManagerJGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
+ BackUpManagerJGit backupManagerJGit = new BackUpManagerJGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ backupManagerJGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
+ runningInstances.add(backupManagerJGit);
+ return backupManagerJGit;
+ }
+ /**
+ * Shuts down the BackupManager which is associated with the given {@link BibDatabaseContext}.
+ *
+ * @param bibDatabaseContext Associated {@link BibDatabaseContext}
+ * @param createBackup True, if a backup should be created
+ * @param backupDir The path to the backup directory
+ */
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdown(backupDir, createBackup));
+ runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
+ }
+ /**
+ * Checks whether a backup file exists for the given database file. If it exists, it is checked whether it is
+ * newer and different from the original.
+ *
+ * In case a discarded file is present, the method also returns false
, See also {@link #discardBackup(Path)}.
+ *
+ * @param originalPath Path to the file a backup should be checked for. Example: jabref.bib.
+ *
+ * @return true
if backup file exists AND differs from originalPath. false
is the
+ * "default" return value in the good case. In case a discarded file exists, false
is returned, too.
+ * In the case of an exception true
is returned to ensure that the user checks the output.
+ */
+ public static boolean backupGitDiffers(Path originalPath, Path backupDir) {
+ //à implementer
+ Path discardedFile = determineDiscardedFile(originalPath, backupDir);
+ if (Files.exists(discardedFile)) {
+ try {
+ Files.delete(discardedFile);
+ } catch (
+ IOException e) {
+ LOGGER.error("Could not remove discarded file {}", discardedFile, e);
+ return true;
+ }
+ return false;
+ }
+ return getLatestBackupPath(originalPath, backupDir).map(latestBackupPath -> {
+ FileTime latestBackupFileLastModifiedTime;
+ try {
+ latestBackupFileLastModifiedTime = Files.getLastModifiedTime(latestBackupPath);
+ } catch (IOException e) {
+ LOGGER.debug("Could not get timestamp of backup file {}", latestBackupPath, e);
+ // If we cannot get the timestamp, we do show any warning
+ return false;
+ }
+ FileTime currentFileLastModifiedTime;
+ try {
+ currentFileLastModifiedTime = Files.getLastModifiedTime(originalPath);
+ } catch (IOException e) {
+ LOGGER.debug("Could not get timestamp of current file file {}", originalPath, e);
+ // If we cannot get the timestamp, we do show any warning
+ return false;
+ }
+ if (latestBackupFileLastModifiedTime.compareTo(currentFileLastModifiedTime) <= 0) {
+ // Backup is older than current file
+ // We treat the backup as non-different (even if it could differ)
+ return false;
+ }
+ try {
+ boolean result = Files.mismatch(originalPath, latestBackupPath) != -1L;
+ if (result) {
+ LOGGER.info("Backup file {} differs from current file {}", latestBackupPath, originalPath);
+ }
+ return result;
+ } catch (IOException e) {
+ LOGGER.debug("Could not compare original file and backup file.", e);
+ // User has to investigate in this case
+ return true;
+ }
+ }).orElse(false);
+ }
+ /**
+ * Restores the backup file by copying and overwriting the original one.
+ *
+ * @param originalPath Path to the file which should be equalized to the backup file.
+ */
+ public static void restoreBackup(Path originalPath, Path backupDir) {
+ Optional backupPath = getLatestBackupPath(originalPath, backupDir);
+ if (backupPath.isEmpty()) {
+ LOGGER.error("There is no backup file");
+ return;
+ }
+ try {
+ Files.copy(backupPath.get(), originalPath, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ LOGGER.error("Error while restoring the backup file.", e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
index acae02c01c8..849ecbccac7 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
@@ -236,7 +236,9 @@ void performBackup(Path backupPath) {
LOGGER.error("Could not delete backup file {}", oldestBackupFile, e);
}
}
-
+ //l'ordre dans lequel les entrées BibTeX doivent être écrites dans le fichier de sauvegarde.
+ // Si l'utilisateur a trié la table d'affichage des entrées dans JabRef, cet ordre est récupéré.
+ // Sinon, un ordre par défaut est utilisé.
// code similar to org.jabref.gui.exporter.SaveDatabaseAction.saveDatabase
SelfContainedSaveOrder saveOrder = bibDatabaseContext
.getMetaData().getSaveOrder()
@@ -256,6 +258,10 @@ void performBackup(Path backupPath) {
}
})
.orElse(SaveOrder.getDefaultSaveOrder());
+
+ //Elle configure la sauvegarde, en indiquant qu'aucune sauvegarde supplémentaire (backup) ne doit être créée,
+ // que l'ordre de sauvegarde doit être celui défini, et que les entrées doivent être formatées selon les préférences
+ // utilisateur.
SelfContainedSaveConfiguration saveConfiguration = (SelfContainedSaveConfiguration) new SelfContainedSaveConfiguration()
.withMakeBackup(false)
.withSaveOrder(saveOrder)
@@ -263,13 +269,15 @@ void performBackup(Path backupPath) {
// "Clone" the database context
// We "know" that "only" the BibEntries might be changed during writing (see [org.jabref.logic.exporter.BibDatabaseWriter.savePartOfDatabase])
+ //Chaque entrée BibTeX (comme un article, livre, etc.) est clonée en utilisant la méthode clone().
+ // Cela garantit que les modifications faites pendant la sauvegarde n'affecteront pas l'entrée originale.
List list = bibDatabaseContext.getDatabase().getEntries().stream()
.map(BibEntry::clone)
.map(BibEntry.class::cast)
.toList();
BibDatabase bibDatabaseClone = new BibDatabase(list);
BibDatabaseContext bibDatabaseContextClone = new BibDatabaseContext(bibDatabaseClone, bibDatabaseContext.getMetaData());
-
+ //Elle définit l'encodage à utiliser pour écrire le fichier. Cela garantit que les caractères spéciaux sont bien sauvegardés.
Charset encoding = bibDatabaseContext.getMetaData().getEncoding().orElse(StandardCharsets.UTF_8);
// We want to have successful backups only
// Thus, we do not use a plain "FileWriter", but the "AtomicFileWriter"
@@ -335,7 +343,7 @@ public synchronized void listen(@SuppressWarnings("unused") BibDatabaseContextCh
}
private void startBackupTask(Path backupDir) {
- fillQueue(backupDir);
+ fillQueue(backupDir);//remplie backupFilesQueue les files .sav de le meme bibl
executor.scheduleAtFixedRate(
// We need to determine the backup path on each action, because we use the timestamp in the filename
@@ -344,7 +352,8 @@ private void startBackupTask(Path backupDir) {
DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
TimeUnit.SECONDS);
}
-
+//La méthode fillQueue(backupDir) est définie dans le code et son rôle est de lister et d'ajouter
+// les fichiers de sauvegarde existants dans une file d'attente,
private void fillQueue(Path backupDir) {
if (!Files.exists(backupDir)) {
return;
@@ -355,7 +364,7 @@ private void fillQueue(Path backupDir) {
try {
List allSavFiles = Files.list(backupDir)
// just list the .sav belonging to the given targetFile
- .filter(p -> p.getFileName().toString().startsWith(prefix))
+ .filter(p -> p.getFileName().toString().startsWith(prefix))//tous les files .sav commencerait par ce prefix
.sorted().toList();
backupFilesQueue.addAll(allSavFiles);
} catch (IOException e) {
From d0f6346a0075c515c6547ee2ebc31bb93278d538 Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Wed, 23 Oct 2024 20:20:12 +0200
Subject: [PATCH 02/84] l'ajout de la classe BackUpManagerJGit
---
.../gui/autosaveandbackup/BackUpManagerJGit.java | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
index 13cf7b03628..8ff36227d25 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
@@ -90,16 +90,7 @@ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDi
*/
public static boolean backupGitDiffers(Path originalPath, Path backupDir) {
//à implementer
- Path discardedFile = determineDiscardedFile(originalPath, backupDir);
- if (Files.exists(discardedFile)) {
- try {
- Files.delete(discardedFile);
- } catch (
- IOException e) {
- LOGGER.error("Could not remove discarded file {}", discardedFile, e);
- return true;
- }
- return false;
+
}
return getLatestBackupPath(originalPath, backupDir).map(latestBackupPath -> {
FileTime latestBackupFileLastModifiedTime;
From 9ab37a2e9af9f9750d5793427b8c985b68180477 Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Wed, 23 Oct 2024 20:29:39 +0200
Subject: [PATCH 03/84] l'ajout de la classe BackUpManagerJGit
---
.../gui/autosaveandbackup/BackUpManagerJGit.java | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
index 8ff36227d25..90d99ef2894 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
@@ -133,16 +133,9 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) {
* @param originalPath Path to the file which should be equalized to the backup file.
*/
public static void restoreBackup(Path originalPath, Path backupDir) {
- Optional backupPath = getLatestBackupPath(originalPath, backupDir);
- if (backupPath.isEmpty()) {
- LOGGER.error("There is no backup file");
- return;
- }
- try {
- Files.copy(backupPath.get(), originalPath, StandardCopyOption.REPLACE_EXISTING);
- } catch (IOException e) {
- LOGGER.error("Error while restoring the backup file.", e);
- }
+ /**
+ * à implementer
+ * */
}
}
From d8812efe196f53269189c3c6088aa92923fd2e47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 6 Nov 2024 10:14:56 +0100
Subject: [PATCH 04/84] Add the class JGIT
---
.../org/jabref/gui/autosaveandbackup/BackupManagerJGit.java | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
new file mode 100644
index 00000000000..19a82b2b8b2
--- /dev/null
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -0,0 +1,4 @@
+package org.jabref.gui.autosaveandbackup;
+
+public class BackupManagerJGit {
+}
From af50c7c70d7ee65bf4d1ad271a186078c7be5eb6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 6 Nov 2024 10:39:13 +0100
Subject: [PATCH 05/84] first git implementation, not tested
---
.../autosaveandbackup/BackupManagerJGit.java | 125 ++++++++++++++++++
1 file changed, 125 insertions(+)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index 19a82b2b8b2..ce4b032645a 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -1,4 +1,129 @@
package org.jabref.gui.autosaveandbackup;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.jabref.gui.LibraryTab;
+import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.logic.util.CoarseChangeFilter;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntryTypesManager;
+
public class BackupManagerJGit {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackupManager.class);
+
+ private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
+
+ private static Set runningInstances = new HashSet();
+
+ private final BibDatabaseContext bibDatabaseContext;
+ private final CliPreferences preferences;
+ private final ScheduledThreadPoolExecutor executor;
+ private final CoarseChangeFilter changeFilter;
+ private final BibEntryTypesManager entryTypesManager;
+ private final LibraryTab libraryTab;
+ private final Git git;
+
+ private boolean needsBackup = false;
+
+ BackupManagerJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ this.bibDatabaseContext = bibDatabaseContext;
+ this.entryTypesManager = entryTypesManager;
+ this.preferences = preferences;
+ this.executor = new ScheduledThreadPoolExecutor(2);
+ this.libraryTab = libraryTab;
+
+ changeFilter = new CoarseChangeFilter(bibDatabaseContext);
+ changeFilter.registerListener(this);
+
+ // Initialize Git repository
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
+ git = new Git(builder.setGitDir(new File(preferences.getFilePreferences().getBackupDirectory().toFile(), ".git"))
+ .readEnvironment()
+ .findGitDir()
+ .build());
+ if (git.getRepository().getObjectDatabase().exists()) {
+ LOGGER.info("Git repository already exists");
+ } else {
+ git.init().call();
+ LOGGER.info("Initialized new Git repository");
+ }
+ }
+
+ public static BackupManagerJGit startJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ BackupManagerJGit backupManagerJGit = new BackupManagerJGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ backupManagerJGit.startBackupTaskJGit(preferences.getFilePreferences().getBackupDirectory());
+ runningInstances.add(backupManagerJGit);
+ return backupManagerJGit;
+ }
+
+ public static void shutdownJGit(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup));
+ runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
+ }
+
+ private void startBackupTaskJGit(Path backupDir) {
+ executor.scheduleAtFixedRate(
+ () -> {
+ try {
+ performBackup(backupDir);
+ } catch (IOException | GitAPIException e) {
+ LOGGER.error("Error during backup", e);
+ }
+ },
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ TimeUnit.SECONDS);
+ }
+
+ private void performBackup(Path backupDir) throws IOException, GitAPIException {
+ if (!needsBackup) {
+ return;
+ }
+
+ // Add and commit changes
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
+ LOGGER.info("Committed backup: {}", commit.getId());
+
+ // Reset the backup flag
+ this.needsBackup = false;
+ }
+
+ public static void restoreBackup(Path originalPath, Path backupDir) {
+ try {
+ Git git = Git.open(backupDir.toFile());
+ git.checkout().setName("HEAD").call();
+ LOGGER.info("Restored backup from Git repository");
+ } catch (IOException | GitAPIException e) {
+ LOGGER.error("Error while restoring the backup", e);
+ }
+ }
+
+ private void shutdownJGit(Path backupDir, boolean createBackup) {
+ changeFilter.unregisterListener(this);
+ changeFilter.shutdown();
+ executor.shutdown();
+
+ if (createBackup) {
+ try {
+ performBackup(backupDir);
+ } catch (IOException | GitAPIException e) {
+ LOGGER.error("Error during shutdown backup", e);
+ }
+ }
+ }
}
From e72d40803790b98650cf3179b2cf9a49258dbea0 Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Wed, 6 Nov 2024 14:12:10 +0100
Subject: [PATCH 06/84] ajout de la methide backupGitDiffers
---
buildres/abbrv.jabref.org | 2 +-
jabref | 1 +
.../autosaveandbackup/BackupManagerJGit.java | 50 ++++++++++++++++---
src/main/resources/csl-locales | 2 +-
src/main/resources/csl-styles | 2 +-
5 files changed, 46 insertions(+), 11 deletions(-)
create mode 160000 jabref
diff --git a/buildres/abbrv.jabref.org b/buildres/abbrv.jabref.org
index 0fdf99147a8..d87037495de 160000
--- a/buildres/abbrv.jabref.org
+++ b/buildres/abbrv.jabref.org
@@ -1 +1 @@
-Subproject commit 0fdf99147a8a5fc8ae7ccd79ad4e0029e736e4a3
+Subproject commit d87037495de7213b896dbb6a20170387de170709
diff --git a/jabref b/jabref
new file mode 160000
index 00000000000..4705977685c
--- /dev/null
+++ b/jabref
@@ -0,0 +1 @@
+Subproject commit 4705977685c6b0551a8d40458abced473501d245
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index ce4b032645a..e2d8d3e047d 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -1,16 +1,10 @@
package org.jabref.gui.autosaveandbackup;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -21,9 +15,20 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
public class BackupManagerJGit {
- private static final Logger LOGGER = LoggerFactory.getLogger(BackupManager.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackupManagerJGit.class);
private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
@@ -90,6 +95,10 @@ private void startBackupTaskJGit(Path backupDir) {
}
private void performBackup(Path backupDir) throws IOException, GitAPIException {
+ /*
+
+ il faut initialiser needsBackup
+ */
if (!needsBackup) {
return;
}
@@ -113,6 +122,31 @@ public static void restoreBackup(Path originalPath, Path backupDir) {
}
}
+ /*
+ compare what is in originalPath and last commit
+ */
+
+ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
+
+ File repoDir = backupDir.toFile();
+ Repository repository = new FileRepositoryBuilder()
+ .setGitDir(new File(repoDir, ".git"))
+ .build();
+ try (Git git = new Git(repository)) {
+ ObjectId headCommitId = repository.resolve("HEAD"); // to get the latest commit id
+ if (headCommitId == null) {
+ // No commits in the repository, so there's no previous backup
+ return true;
+ }
+ git.add().addFilepattern(originalPath.getFileName().toString()).call();
+ String relativePath = backupDir.relativize(originalPath).toString();
+ List diffs = git.diff()
+ .setPathFilter(PathFilter.create(relativePath)) // Utiliser PathFilter ici
+ .call();
+ return !diffs.isEmpty();
+ }
+ }
+
private void shutdownJGit(Path backupDir, boolean createBackup) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
diff --git a/src/main/resources/csl-locales b/src/main/resources/csl-locales
index 4753e3a9aca..8bc2af16f51 160000
--- a/src/main/resources/csl-locales
+++ b/src/main/resources/csl-locales
@@ -1 +1 @@
-Subproject commit 4753e3a9aca4b806ac0e3036ed727d47bf8f678e
+Subproject commit 8bc2af16f5180a8e4fb591c2be916650f75bb8f6
diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles
index 49af15c4f5b..b413a778b81 160000
--- a/src/main/resources/csl-styles
+++ b/src/main/resources/csl-styles
@@ -1 +1 @@
-Subproject commit 49af15c4f5bca025b6b18ca48c447016586f01e7
+Subproject commit b413a778b8170cf5aebbb9aeffec62cfd068e19e
From d427e058178240b5520bdf5d7f20a00f91d8843f Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Wed, 6 Nov 2024 14:26:07 +0100
Subject: [PATCH 07/84] Added Pop up to choose backup
---
.../jabref/gui/backup/BackupChoiceDialog.java | 94 +++++++++++++++++++
.../org/jabref/gui/backup/BackupEntry.java | 42 +++++++++
.../gui/backup/BackupResolverDialog.java | 7 +-
.../jabref/gui/dialogs/BackupUIManager.java | 17 +++-
.../jabref/logic/bst/BstVMVisitorTest.java | 8 +-
.../importer/fileformat/CffImporterTest.java | 8 +-
.../io/CitationKeyBasedFileFinderTest.java | 4 +-
7 files changed, 166 insertions(+), 14 deletions(-)
create mode 100644 src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
create mode 100644 src/main/java/org/jabref/gui/backup/BackupEntry.java
diff --git a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
new file mode 100644
index 00000000000..bf0b729ba6d
--- /dev/null
+++ b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
@@ -0,0 +1,94 @@
+package org.jabref.gui.backup;
+
+import java.nio.file.Path;
+import java.util.Optional;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.ButtonBar;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.Label;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.VBox;
+
+import org.jabref.gui.FXDialog;
+import org.jabref.gui.collab.DatabaseChange;
+import org.jabref.gui.frame.ExternalApplicationsPreferences;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.util.BackupFileType;
+import org.jabref.logic.util.io.BackupFileUtil;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BackupChoiceDialog extends FXDialog {
+ public static final ButtonType RESTORE_BACKUP = new ButtonType(Localization.lang("Restore from backup"), ButtonBar.ButtonData.OK_DONE);
+ public static final ButtonType IGNORE_BACKUP = new ButtonType(Localization.lang("Ignore backup"), ButtonBar.ButtonData.CANCEL_CLOSE);
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackupResolverDialog.class);
+
+ @FXML
+ private TableView backupTableView;
+
+ public BackupChoiceDialog(Path originalPath, Path backupDir, ExternalApplicationsPreferences externalApplicationsPreferences) {
+ super(AlertType.CONFIRMATION, Localization.lang("Choose backup"), true);
+ setHeaderText(null);
+ getDialogPane().setMinHeight(180);
+ getDialogPane().getButtonTypes().setAll(RESTORE_BACKUP, IGNORE_BACKUP);
+
+ Optional backupPathOpt = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
+ String backupFilename = backupPathOpt.map(Path::getFileName).map(Path::toString).orElse(Localization.lang("File not found"));
+ String content = Localization.lang("The :") + "\n" +
+ Localization.lang("Here are some backup versions you can revert to");
+
+ // Create a TableView for backups
+ TableView backupTableView = new TableView<>();
+ // Define columns
+ TableColumn dateColumn = new TableColumn<>("Date of Backup");
+ dateColumn.setCellValueFactory(cellData -> cellData.getValue().dateProperty());
+
+ TableColumn sizeColumn = new TableColumn<>("Size of Backup");
+ sizeColumn.setCellValueFactory(cellData -> cellData.getValue().sizeProperty());
+
+ TableColumn entriesColumn = new TableColumn<>("Number of Entries");
+ entriesColumn.setCellValueFactory(cellData -> cellData.getValue().entriesProperty().asObject());
+
+ // Add columns to the table
+ backupTableView.getColumns().addAll(dateColumn, sizeColumn, entriesColumn);
+
+ // Sample data
+ ObservableList data = FXCollections.observableArrayList(
+ new BackupEntry("2023-11-01", "500 MB", 120),
+ new BackupEntry("2023-10-15", "300 MB", 80),
+ new BackupEntry("2023-10-01", "250 MB", 60),
+ new BackupEntry("2023-11-01", "500 MB", 120),
+ new BackupEntry("2023-10-15", "300 MB", 80),
+ new BackupEntry("2023-11-01", "500 MB", 120),
+ new BackupEntry("2023-10-15", "300 MB", 80),
+ new BackupEntry("2023-11-01", "500 MB", 120),
+ new BackupEntry("2023-10-15", "300 MB", 80),
+ new BackupEntry("2023-11-01", "500 MB", 120),
+ new BackupEntry("2023-10-15", "300 MB", 80),
+ new BackupEntry("2023-11-01", "500 MB", 120),
+ new BackupEntry("2023-10-15", "300 MB", 80),
+ new BackupEntry("2023-11-01", "500 MB", 120),
+ new BackupEntry("2023-10-15", "300 MB", 80),
+ new BackupEntry("2023-11-01", "500 MB", 120),
+ new BackupEntry("2023-10-15", "300 MB", 80)
+ );
+
+ backupTableView.setItems(data);
+
+ setContentText(content);
+
+ // Create a VBox to hold the table and additional content
+ VBox contentBox = new VBox(10);
+ contentBox.getChildren().addAll(new Label(content), backupTableView);
+
+ // Add the VBox to the dialog's content
+ getDialogPane().setContent(contentBox);
+ }
+}
+
diff --git a/src/main/java/org/jabref/gui/backup/BackupEntry.java b/src/main/java/org/jabref/gui/backup/BackupEntry.java
new file mode 100644
index 00000000000..9a6c3fe817c
--- /dev/null
+++ b/src/main/java/org/jabref/gui/backup/BackupEntry.java
@@ -0,0 +1,42 @@
+package org.jabref.gui.backup;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+public class BackupEntry {
+ private final StringProperty date;
+ private final StringProperty size;
+ private final IntegerProperty entries;
+
+ public BackupEntry(String date, String size, int entries) {
+ this.date = new SimpleStringProperty(date);
+ this.size = new SimpleStringProperty(size);
+ this.entries = new SimpleIntegerProperty(entries);
+ }
+
+ public String getDate() {
+ return date.get();
+ }
+
+ public StringProperty dateProperty() {
+ return date;
+ }
+
+ public String getSize() {
+ return size.get();
+ }
+
+ public StringProperty sizeProperty() {
+ return size;
+ }
+
+ public int getEntries() {
+ return entries.get();
+ }
+
+ public IntegerProperty entriesProperty() {
+ return entries;
+ }
+}
diff --git a/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
index cf35336765d..c7aad7811ba 100644
--- a/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
+++ b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
@@ -20,9 +20,10 @@
import org.slf4j.LoggerFactory;
public class BackupResolverDialog extends FXDialog {
- public static final ButtonType RESTORE_FROM_BACKUP = new ButtonType(Localization.lang("Restore from backup"), ButtonBar.ButtonData.OK_DONE);
- public static final ButtonType REVIEW_BACKUP = new ButtonType(Localization.lang("Review backup"), ButtonBar.ButtonData.LEFT);
+ public static final ButtonType RESTORE_FROM_BACKUP = new ButtonType(Localization.lang("Restore from latest backup"), ButtonBar.ButtonData.OK_DONE);
+ public static final ButtonType REVIEW_BACKUP = new ButtonType(Localization.lang("Review latest backup"), ButtonBar.ButtonData.LEFT);
public static final ButtonType IGNORE_BACKUP = new ButtonType(Localization.lang("Ignore backup"), ButtonBar.ButtonData.CANCEL_CLOSE);
+ public static final ButtonType COMPARE_OLDER_BACKUP = new ButtonType("Compare older backup", ButtonBar.ButtonData.LEFT);
private static final Logger LOGGER = LoggerFactory.getLogger(BackupResolverDialog.class);
@@ -30,7 +31,7 @@ public BackupResolverDialog(Path originalPath, Path backupDir, ExternalApplicati
super(AlertType.CONFIRMATION, Localization.lang("Backup found"), true);
setHeaderText(null);
getDialogPane().setMinHeight(180);
- getDialogPane().getButtonTypes().setAll(RESTORE_FROM_BACKUP, REVIEW_BACKUP, IGNORE_BACKUP);
+ getDialogPane().getButtonTypes().setAll(RESTORE_FROM_BACKUP, REVIEW_BACKUP, IGNORE_BACKUP, COMPARE_OLDER_BACKUP);
Optional backupPathOpt = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
String backupFilename = backupPathOpt.map(Path::getFileName).map(Path::toString).orElse(Localization.lang("File not found"));
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 08dd120ab78..b02561cd5ef 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -13,6 +13,7 @@
import org.jabref.gui.LibraryTab;
import org.jabref.gui.StateManager;
import org.jabref.gui.autosaveandbackup.BackupManager;
+import org.jabref.gui.backup.BackupChoiceDialog;
import org.jabref.gui.backup.BackupResolverDialog;
import org.jabref.gui.collab.DatabaseChange;
import org.jabref.gui.collab.DatabaseChangeList;
@@ -59,9 +60,15 @@ public static Optional showRestoreBackupDialog(DialogService dialo
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
return Optional.empty();
+ } else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
+ var actions = showBackupChoiceDialog(
+ dialogService,
+ preferences.getExternalApplicationsPreferences(),
+ originalPath,
+ preferences.getFilePreferences().getBackupDirectory());
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
- }
+ }
return Optional.empty();
});
}
@@ -74,6 +81,14 @@ private static Optional showBackupResolverDialog(DialogService dialo
() -> dialogService.showCustomDialogAndWait(new BackupResolverDialog(originalPath, backupDir, externalApplicationsPreferences)));
}
+ private static Optional showBackupChoiceDialog(DialogService dialogService,
+ ExternalApplicationsPreferences externalApplicationsPreferences,
+ Path originalPath,
+ Path backupDir) {
+ return UiTaskExecutor.runInJavaFXThread(
+ () -> dialogService.showCustomDialogAndWait(new BackupChoiceDialog(originalPath, backupDir, externalApplicationsPreferences)));
+ }
+
private static Optional showReviewBackupDialog(
DialogService dialogService,
Path originalPath,
diff --git a/src/test/java/org/jabref/logic/bst/BstVMVisitorTest.java b/src/test/java/org/jabref/logic/bst/BstVMVisitorTest.java
index c8c57f250db..23c0f7e0eed 100644
--- a/src/test/java/org/jabref/logic/bst/BstVMVisitorTest.java
+++ b/src/test/java/org/jabref/logic/bst/BstVMVisitorTest.java
@@ -203,8 +203,8 @@ void visitIdentifier() {
FUNCTION { test } {
#1 'local.variable :=
#2 'variable :=
- "TEST" 'local.label :=
- "TEST-GLOBAL" 'label :=
+ "COMPARE_OLDER_BACKUP" 'local.label :=
+ "COMPARE_OLDER_BACKUP-GLOBAL" 'label :=
local.label local.variable
label variable
}
@@ -215,9 +215,9 @@ void visitIdentifier() {
vm.render(testEntries);
assertEquals(2, vm.getStack().pop());
- assertEquals("TEST-GLOBAL", vm.getStack().pop());
+ assertEquals("COMPARE_OLDER_BACKUP-GLOBAL", vm.getStack().pop());
assertEquals(1, vm.getStack().pop());
- assertEquals("TEST", vm.getStack().pop());
+ assertEquals("COMPARE_OLDER_BACKUP", vm.getStack().pop());
assertEquals(0, vm.getStack().size());
}
diff --git a/src/test/java/org/jabref/logic/importer/fileformat/CffImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/CffImporterTest.java
index 195d7bd713b..bca58a77401 100644
--- a/src/test/java/org/jabref/logic/importer/fileformat/CffImporterTest.java
+++ b/src/test/java/org/jabref/logic/importer/fileformat/CffImporterTest.java
@@ -175,7 +175,7 @@ void importEntriesPreferredCitation() throws IOException, URISyntaxException {
BibEntry expectedPreferred = new BibEntry(StandardEntryType.InProceedings)
.withCitationKey(citeKey)
.withField(StandardField.AUTHOR, "Jonathan von Duke and Jim Kingston, Jr.")
- .withField(StandardField.DOI, "10.0001/TEST")
+ .withField(StandardField.DOI, "10.0001/COMPARE_OLDER_BACKUP")
.withField(StandardField.URL, "www.github.com");
assertEquals(mainEntry, expectedMain);
@@ -198,13 +198,13 @@ void importEntriesReferences() throws IOException, URISyntaxException {
.withCitationKey(citeKey1)
.withField(StandardField.AUTHOR, "Jonathan von Duke and Jim Kingston, Jr.")
.withField(StandardField.YEAR, "2007")
- .withField(StandardField.DOI, "10.0001/TEST")
+ .withField(StandardField.DOI, "10.0001/COMPARE_OLDER_BACKUP")
.withField(StandardField.URL, "www.example.com");
BibEntry expectedReference2 = new BibEntry(StandardEntryType.Manual)
.withCitationKey(citeKey2)
.withField(StandardField.AUTHOR, "Arthur Clark, Jr. and Luca von Diamond")
- .withField(StandardField.DOI, "10.0002/TEST")
+ .withField(StandardField.DOI, "10.0002/COMPARE_OLDER_BACKUP")
.withField(StandardField.URL, "www.facebook.com");
assertEquals(mainEntry, expectedMain);
@@ -218,7 +218,7 @@ public BibEntry getPopulatedEntry() {
.withField(StandardField.TITLE, "Test")
.withField(StandardField.URL, "www.google.com")
.withField(BiblatexSoftwareField.REPOSITORY, "www.github.com")
- .withField(StandardField.DOI, "10.0000/TEST")
+ .withField(StandardField.DOI, "10.0000/COMPARE_OLDER_BACKUP")
.withField(StandardField.DATE, "2000-07-02")
.withField(StandardField.COMMENT, "Test entry.")
.withField(StandardField.ABSTRACT, "Test abstract.")
diff --git a/src/test/java/org/jabref/logic/util/io/CitationKeyBasedFileFinderTest.java b/src/test/java/org/jabref/logic/util/io/CitationKeyBasedFileFinderTest.java
index 3f29211f4e0..7133acaa60e 100644
--- a/src/test/java/org/jabref/logic/util/io/CitationKeyBasedFileFinderTest.java
+++ b/src/test/java/org/jabref/logic/util/io/CitationKeyBasedFileFinderTest.java
@@ -47,8 +47,8 @@ void setUp(@TempDir Path temporaryFolder) throws IOException {
Files.createFile(dir2003.resolve("Paper by HipKro03.pdf"));
Path dirTest = Files.createDirectory(rootDir.resolve("test"));
- Files.createFile(dirTest.resolve(".TEST"));
- Files.createFile(dirTest.resolve("TEST["));
+ Files.createFile(dirTest.resolve(".COMPARE_OLDER_BACKUP"));
+ Files.createFile(dirTest.resolve("COMPARE_OLDER_BACKUP["));
Files.createFile(dirTest.resolve("TE.ST"));
Files.createFile(dirTest.resolve("foo.dat"));
From 0737f485671f8247ab3f0e14ff305f910b47e515 Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Wed, 6 Nov 2024 16:06:41 +0100
Subject: [PATCH 08/84] adding methods 06/11
---
src/main/java/module-info.java | 1 +
.../autosaveandbackup/BackupManagerJGit.java | 40 ++++++++++++++++++-
.../jabref/gui/dialogs/BackupUIManager.java | 3 ++
3 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index f0151b8988e..901beace0e9 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -193,5 +193,6 @@
requires mslinks;
requires org.antlr.antlr4.runtime;
requires org.libreoffice.uno;
+ requires gradle.api;
// endregion
}
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index e2d8d3e047d..d8fc87c3706 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -1,6 +1,8 @@
package org.jabref.gui.autosaveandbackup;
import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashSet;
@@ -18,6 +20,7 @@
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -42,6 +45,7 @@ public class BackupManagerJGit {
private final LibraryTab libraryTab;
private final Git git;
+
private boolean needsBackup = false;
BackupManagerJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
@@ -112,10 +116,11 @@ private void performBackup(Path backupDir) throws IOException, GitAPIException {
this.needsBackup = false;
}
- public static void restoreBackup(Path originalPath, Path backupDir) {
+ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
try {
+
Git git = Git.open(backupDir.toFile());
- git.checkout().setName("HEAD").call();
+ git.checkout().setName(objectId.getName()).call();
LOGGER.info("Restored backup from Git repository");
} catch (IOException | GitAPIException e) {
LOGGER.error("Error while restoring the backup", e);
@@ -147,6 +152,37 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
}
}
+ @SuppressWarnings("checkstyle:RegexpMultiline")
+ public void showDiffersJGit(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
+
+ File repoDir = backupDir.toFile();
+ Repository repository = new FileRepositoryBuilder()
+ .setGitDir(new File(repoDir, ".git"))
+ .build();
+ /*
+ il faut une classe qui affiche les dix dernier backup avec les data: date/ size / number of entries
+ */
+
+ ObjectId oldCommit = repository.resolve(CommitId);
+ ObjectId newCommit = repository.resolve("HEAD");
+
+ FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
+ DiffFormatter diffFr = new DiffFormatter(fos);
+ diffFr.setRepository(repository);
+ diffFr.scan(oldCommit, newCommit);
+ }
+
+
+
+ /*
+
+ faire une methode qui accepte commit id et retourne les diff differences avec la version actuelle
+ methode qui renvoie n derniers indice de commit
+ methode ayant idcommit retourne data
+
+ */
+
+
private void shutdownJGit(Path backupDir, boolean createBackup) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 08dd120ab78..b9f9e21d681 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -22,6 +22,7 @@
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.util.UiTaskExecutor;
+import org.eclipse.jgit.lib.ObjectId;
import org.jabref.logic.importer.ImportFormatPreferences;
import org.jabref.logic.importer.OpenDatabase;
import org.jabref.logic.importer.ParserResult;
@@ -32,6 +33,7 @@
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.jabref.model.util.FileUpdateMonitor;
+import org.gradle.internal.impldep.org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -57,6 +59,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
preferences.getFilePreferences().getBackupDirectory());
return actionOpt.flatMap(action -> {
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
+
BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
return Optional.empty();
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
From f68236fc9e5cb0d1cfbd2a3d37380db518c8af3e Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Thu, 7 Nov 2024 17:41:30 +0100
Subject: [PATCH 09/84] methods aded and changed : retrieveCommitDetails ,
retreiveCommits
---
.../autosaveandbackup/BackupManagerJGit.java | 111 +++++++++++++++++-
1 file changed, 105 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index d8fc87c3706..6ec5a709637 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -5,6 +5,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -24,6 +26,7 @@
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
@@ -116,11 +119,34 @@ private void performBackup(Path backupDir) throws IOException, GitAPIException {
this.needsBackup = false;
}
- public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
+ public static void restoreBackupJGit(Path originalPath, Path backupDir, ObjectId objectId) {
+ try {
+ Git git = Git.open(backupDir.toFile());
+
+ // Extraire le contenu de l'objet spécifié (commit) dans le répertoire de travail
+ git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
+
+ // Ajouter les modifications au staging
+ git.add().addFilepattern(".").call();
+
+ // Faire un commit avec un message explicite
+ git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
+
+ LOGGER.info("Restored backup from Git repository and committed the changes");
+ } catch (IOException | GitAPIException e) {
+ LOGGER.error("Error while restoring the backup", e);
+ }
+ }
+
+ public static void restoreBackupj(Path originalPath, Path backupDir, ObjectId objectId) {
try {
Git git = Git.open(backupDir.toFile());
git.checkout().setName(objectId.getName()).call();
+ /*
+ faut une methode pour evite le branch nouveau
+
+ */
LOGGER.info("Restored backup from Git repository");
} catch (IOException | GitAPIException e) {
LOGGER.error("Error while restoring the backup", e);
@@ -152,8 +178,7 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
}
}
- @SuppressWarnings("checkstyle:RegexpMultiline")
- public void showDiffersJGit(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
+ public List showDiffersJGit(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
@@ -169,14 +194,88 @@ public void showDiffersJGit(Path originalPath, Path backupDir, String CommitId)
FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
DiffFormatter diffFr = new DiffFormatter(fos);
diffFr.setRepository(repository);
- diffFr.scan(oldCommit, newCommit);
+ return diffFr.scan(oldCommit, newCommit);
}
+// n sera un conteur qui incremente de 1 si l'utilisateur a demandé de voir d'autres versions plus anciens(paquet de 10)
+// et decremente de 1 si il veut voir le paquet de 10 versions les plus recentes
+// le scroll bas : n->n+1 ; le scroll en haut : n->n-1
+ public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
+ List retrievedCommits = new ArrayList<>();
+ // Ouvrir le dépôt Git
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
+ // Utiliser RevWalk pour parcourir l'historique des commits
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ // Commencer depuis HEAD
+ RevCommit startCommit = revWalk.parseCommit(repository.resolve("HEAD"));
+ revWalk.markStart(startCommit);
+
+ int count = 0;
+ int startIndex = n * 10;
+ int endIndex = startIndex + 10;
+
+ for (RevCommit commit : revWalk) {
+ // Ignorer les commits jusqu'à l'index de départ
+ if (count < startIndex) {
+ count++;
+ continue;
+ }
+ // Arrêter lorsque nous avons atteint l'index de fin
+ if (count >= endIndex) {
+ break;
+ }
+ // Ajouter les commits à la liste principale
+ retrievedCommits.add(commit);
+ count++;
+ }
+ }
+
+
+ return retrievedCommits;
+ }
+
+ public List> retrieveCommitDetails(List commits, Repository repository) throws IOException, GitAPIException {
+ List> commitDetails = new ArrayList<>();
+
+ // Parcourir la liste des commits fournie en paramètre
+ for (RevCommit commit : commits) {
+ // Liste pour stocker les détails du commit
+ List commitInfo = new ArrayList<>();
+ commitInfo.add(commit.getName()); // ID du commit
+
+ // Récupérer la taille des fichiers modifiés par le commit
+ try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.addTree(commit.getTree());
+ treeWalk.setRecursive(true);
+ long totalSize = 0;
+
+ while (treeWalk.next()) {
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ totalSize += loader.getSize(); // Calculer la taille en octets
+ }
+
+ // Convertir la taille en Ko ou Mo
+ String sizeFormatted = (totalSize > 1024 * 1024)
+ ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
+ : String.format("%.2f Ko", totalSize / 1024.0);
+
+ commitInfo.add(sizeFormatted); // Ajouter la taille formatée
+ }
+
+ // Ajouter la liste des détails à la liste principale
+ commitDetails.add(commitInfo);
+ }
+
+ return commitDetails;
+ }
+
+
+
/*
- faire une methode qui accepte commit id et retourne les diff differences avec la version actuelle
+ faire une methode qui accepte commit id et retourne les diff differences avec la version actuelle( fait)
methode qui renvoie n derniers indice de commit
methode ayant idcommit retourne data
@@ -196,4 +295,4 @@ private void shutdownJGit(Path backupDir, boolean createBackup) {
}
}
}
-}
+
From 129d1756c2e674e2f0ca0f2da4e6492ca8371192 Mon Sep 17 00:00:00 2001
From: Ait Lamine Ilias
Date: Mon, 11 Nov 2024 14:35:19 +0100
Subject: [PATCH 10/84] I start switching French comments to English
---
buildres/abbrv.jabref.org | 2 +-
src/jmh/java/module-info.java | 8 ++++++++
.../jabref/gui/autosaveandbackup/BackupManagerJGit.java | 5 +++--
src/main/resources/csl-styles | 2 +-
4 files changed, 13 insertions(+), 4 deletions(-)
create mode 100644 src/jmh/java/module-info.java
diff --git a/buildres/abbrv.jabref.org b/buildres/abbrv.jabref.org
index d87037495de..28506356e1e 160000
--- a/buildres/abbrv.jabref.org
+++ b/buildres/abbrv.jabref.org
@@ -1 +1 @@
-Subproject commit d87037495de7213b896dbb6a20170387de170709
+Subproject commit 28506356e1eeaef34b65afbf5a85562bc8aaebc3
diff --git a/src/jmh/java/module-info.java b/src/jmh/java/module-info.java
new file mode 100644
index 00000000000..4c0962156dc
--- /dev/null
+++ b/src/jmh/java/module-info.java
@@ -0,0 +1,8 @@
+/**
+ *
+ */
+/**
+ *
+ */
+module JabRef {
+}
\ No newline at end of file
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index 6ec5a709637..43be980fffe 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -6,7 +6,6 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -24,10 +23,12 @@
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -126,7 +127,7 @@ public static void restoreBackupJGit(Path originalPath, Path backupDir, ObjectId
// Extraire le contenu de l'objet spécifié (commit) dans le répertoire de travail
git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
- // Ajouter les modifications au staging
+ // Add modifications to staging area
git.add().addFilepattern(".").call();
// Faire un commit avec un message explicite
diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles
index b413a778b81..8bcd8fb2ee3 160000
--- a/src/main/resources/csl-styles
+++ b/src/main/resources/csl-styles
@@ -1 +1 @@
-Subproject commit b413a778b8170cf5aebbb9aeffec62cfd068e19e
+Subproject commit 8bcd8fb2ee3151e1c5ec2dcf477fa36b26c647c0
From b82682aa2e4f9dc82f5abbe95bb5858c02a58d32 Mon Sep 17 00:00:00 2001
From: Ait Lamine Ilias
Date: Mon, 11 Nov 2024 15:09:33 +0100
Subject: [PATCH 11/84] finish switching comments from French to English
---
.../autosaveandbackup/BackupManagerJGit.java | 47 +++++++++----------
1 file changed, 23 insertions(+), 24 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index 43be980fffe..aa903a52727 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -104,8 +104,7 @@ private void startBackupTaskJGit(Path backupDir) {
private void performBackup(Path backupDir) throws IOException, GitAPIException {
/*
-
- il faut initialiser needsBackup
+ needsBackup must be initialized
*/
if (!needsBackup) {
return;
@@ -124,13 +123,13 @@ public static void restoreBackupJGit(Path originalPath, Path backupDir, ObjectId
try {
Git git = Git.open(backupDir.toFile());
- // Extraire le contenu de l'objet spécifié (commit) dans le répertoire de travail
+ //Extract the content of the object (commit) in the work repository
git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
- // Add modifications to staging area
+ // Add commits to staging Area
git.add().addFilepattern(".").call();
- // Faire un commit avec un message explicite
+ // Commit with a message
git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
LOGGER.info("Restored backup from Git repository and committed the changes");
@@ -146,7 +145,7 @@ public static void restoreBackupj(Path originalPath, Path backupDir, ObjectId ob
git.checkout().setName(objectId.getName()).call();
/*
faut une methode pour evite le branch nouveau
-
+ need a method to avoid the new branch
*/
LOGGER.info("Restored backup from Git repository");
} catch (IOException | GitAPIException e) {
@@ -186,7 +185,7 @@ public List showDiffersJGit(Path originalPath, Path backupDir, String
.setGitDir(new File(repoDir, ".git"))
.build();
/*
- il faut une classe qui affiche les dix dernier backup avec les data: date/ size / number of entries
+ need a class to show the last ten backups indicating: date/ size/ number of entries
*/
ObjectId oldCommit = repository.resolve(CommitId);
@@ -199,16 +198,16 @@ public List showDiffersJGit(Path originalPath, Path backupDir, String
}
-// n sera un conteur qui incremente de 1 si l'utilisateur a demandé de voir d'autres versions plus anciens(paquet de 10)
-// et decremente de 1 si il veut voir le paquet de 10 versions les plus recentes
-// le scroll bas : n->n+1 ; le scroll en haut : n->n-1
+// n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
+// and decrements by 1 when the user asks to see the pack of the 10 earlier versions
+// the scroll down: n->n+1 ; the scroll up: n->n-1
public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
List retrievedCommits = new ArrayList<>();
- // Ouvrir le dépôt Git
+ // Open Git depository
try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- // Utiliser RevWalk pour parcourir l'historique des commits
+ // Use RevWalk to go through all commits
try (RevWalk revWalk = new RevWalk(repository)) {
- // Commencer depuis HEAD
+ // Start from HEAD
RevCommit startCommit = revWalk.parseCommit(repository.resolve("HEAD"));
revWalk.markStart(startCommit);
@@ -217,16 +216,16 @@ public List retreiveCommits(Path backupDir, int n) throws IOException
int endIndex = startIndex + 10;
for (RevCommit commit : revWalk) {
- // Ignorer les commits jusqu'à l'index de départ
+ // Ignore commits before starting index
if (count < startIndex) {
count++;
continue;
}
- // Arrêter lorsque nous avons atteint l'index de fin
+ // Stop at endIndex
if (count >= endIndex) {
break;
}
- // Ajouter les commits à la liste principale
+ // Add commits to the main list
retrievedCommits.add(commit);
count++;
}
@@ -239,13 +238,13 @@ public List retreiveCommits(Path backupDir, int n) throws IOException
public List> retrieveCommitDetails(List commits, Repository repository) throws IOException, GitAPIException {
List> commitDetails = new ArrayList<>();
- // Parcourir la liste des commits fournie en paramètre
+ // Browse the list of commits given as a parameter
for (RevCommit commit : commits) {
- // Liste pour stocker les détails du commit
+ // A list to stock details about the commit
List commitInfo = new ArrayList<>();
- commitInfo.add(commit.getName()); // ID du commit
+ commitInfo.add(commit.getName()); // ID of commit
- // Récupérer la taille des fichiers modifiés par le commit
+ // Get the size of files changes by the commit
try (TreeWalk treeWalk = new TreeWalk(repository)) {
treeWalk.addTree(commit.getTree());
treeWalk.setRecursive(true);
@@ -253,18 +252,18 @@ public List> retrieveCommitDetails(List commits, Reposit
while (treeWalk.next()) {
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- totalSize += loader.getSize(); // Calculer la taille en octets
+ totalSize += loader.getSize(); // size in bytes
}
- // Convertir la taille en Ko ou Mo
+ // Convert the size to Kb or Mb
String sizeFormatted = (totalSize > 1024 * 1024)
? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
: String.format("%.2f Ko", totalSize / 1024.0);
- commitInfo.add(sizeFormatted); // Ajouter la taille formatée
+ commitInfo.add(sizeFormatted); // Add Formatted size
}
- // Ajouter la liste des détails à la liste principale
+ // Add list of details to the main list
commitDetails.add(commitInfo);
}
From 34a288922f50576904e89e297290742435c9a02d Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Tue, 12 Nov 2024 19:09:00 +0100
Subject: [PATCH 12/84] Added table Removed BackupManagerJGit to do UI changes
---
.../autosaveandbackup/BackUpManagerJGit.java | 141 ------------------
.../jabref/gui/backup/BackupChoiceDialog.java | 57 +++----
.../jabref/gui/dialogs/BackupUIManager.java | 26 ++--
3 files changed, 40 insertions(+), 184 deletions(-)
delete mode 100644 src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
deleted file mode 100644
index 90d99ef2894..00000000000
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackUpManagerJGit.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package org.jabref.gui.autosaveandbackup;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.FileTime;
-import java.util.HashSet;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-
-import org.jabref.gui.LibraryTab;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.util.CoarseChangeFilter;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntryTypesManager;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class BackUpManagerJGit {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(BackupManager.class);
-
-
- private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
-
- private static Set runningInstances = new HashSet<>();
-
- private final BibDatabaseContext bibDatabaseContext;
- private final CliPreferences preferences;
- private final ScheduledThreadPoolExecutor executor;
- private final CoarseChangeFilter changeFilter;
- private final BibEntryTypesManager entryTypesManager;
- private final LibraryTab libraryTab;
-
- // Contains a list of all backup paths
- // During writing, the less recent backup file is deleted
- //private final Queue backupFilesQueue = new LinkedBlockingQueue<>();
- private boolean needsBackup = false;
-
- public BackUpManagerJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
- this.bibDatabaseContext = bibDatabaseContext;
- this.entryTypesManager = entryTypesManager;
- this.preferences = preferences;
- this.executor = new ScheduledThreadPoolExecutor(2);
- this.libraryTab = libraryTab;
-
- changeFilter = new CoarseChangeFilter(bibDatabaseContext);
- changeFilter.registerListener(this);
- }
- /**
- * Starts the BackupManager which is associated with the given {@link BibDatabaseContext}. As long as no database
- * file is present in {@link BibDatabaseContext}, the {@link BackupManager} will do nothing.
- *
- * This method is not thread-safe. The caller has to ensure that this method is not called in parallel.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- */
-
- public static BackUpManagerJGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
- BackUpManagerJGit backupManagerJGit = new BackUpManagerJGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManagerJGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
- runningInstances.add(backupManagerJGit);
- return backupManagerJGit;
- }
- /**
- * Shuts down the BackupManager which is associated with the given {@link BibDatabaseContext}.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- * @param createBackup True, if a backup should be created
- * @param backupDir The path to the backup directory
- */
- public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdown(backupDir, createBackup));
- runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
- }
- /**
- * Checks whether a backup file exists for the given database file. If it exists, it is checked whether it is
- * newer and different from the original.
- *
- * In case a discarded file is present, the method also returns false
, See also {@link #discardBackup(Path)}.
- *
- * @param originalPath Path to the file a backup should be checked for. Example: jabref.bib.
- *
- * @return true
if backup file exists AND differs from originalPath. false
is the
- * "default" return value in the good case. In case a discarded file exists, false
is returned, too.
- * In the case of an exception true
is returned to ensure that the user checks the output.
- */
- public static boolean backupGitDiffers(Path originalPath, Path backupDir) {
- //à implementer
-
- }
- return getLatestBackupPath(originalPath, backupDir).map(latestBackupPath -> {
- FileTime latestBackupFileLastModifiedTime;
- try {
- latestBackupFileLastModifiedTime = Files.getLastModifiedTime(latestBackupPath);
- } catch (IOException e) {
- LOGGER.debug("Could not get timestamp of backup file {}", latestBackupPath, e);
- // If we cannot get the timestamp, we do show any warning
- return false;
- }
- FileTime currentFileLastModifiedTime;
- try {
- currentFileLastModifiedTime = Files.getLastModifiedTime(originalPath);
- } catch (IOException e) {
- LOGGER.debug("Could not get timestamp of current file file {}", originalPath, e);
- // If we cannot get the timestamp, we do show any warning
- return false;
- }
- if (latestBackupFileLastModifiedTime.compareTo(currentFileLastModifiedTime) <= 0) {
- // Backup is older than current file
- // We treat the backup as non-different (even if it could differ)
- return false;
- }
- try {
- boolean result = Files.mismatch(originalPath, latestBackupPath) != -1L;
- if (result) {
- LOGGER.info("Backup file {} differs from current file {}", latestBackupPath, originalPath);
- }
- return result;
- } catch (IOException e) {
- LOGGER.debug("Could not compare original file and backup file.", e);
- // User has to investigate in this case
- return true;
- }
- }).orElse(false);
- }
- /**
- * Restores the backup file by copying and overwriting the original one.
- *
- * @param originalPath Path to the file which should be equalized to the backup file.
- */
- public static void restoreBackup(Path originalPath, Path backupDir) {
- /**
- * à implementer
- * */
- }
-
-}
diff --git a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
index bf0b729ba6d..84d45f0aeaa 100644
--- a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
+++ b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
@@ -13,9 +13,9 @@
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
-import org.jabref.gui.FXDialog;
import org.jabref.gui.collab.DatabaseChange;
import org.jabref.gui.frame.ExternalApplicationsPreferences;
+import org.jabref.gui.util.BaseDialog;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.BackupFileType;
import org.jabref.logic.util.io.BackupFileUtil;
@@ -23,62 +23,46 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class BackupChoiceDialog extends FXDialog {
+public class BackupChoiceDialog extends BaseDialog {
public static final ButtonType RESTORE_BACKUP = new ButtonType(Localization.lang("Restore from backup"), ButtonBar.ButtonData.OK_DONE);
public static final ButtonType IGNORE_BACKUP = new ButtonType(Localization.lang("Ignore backup"), ButtonBar.ButtonData.CANCEL_CLOSE);
- private static final Logger LOGGER = LoggerFactory.getLogger(BackupResolverDialog.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackupChoiceDialog.class);
@FXML
private TableView backupTableView;
public BackupChoiceDialog(Path originalPath, Path backupDir, ExternalApplicationsPreferences externalApplicationsPreferences) {
- super(AlertType.CONFIRMATION, Localization.lang("Choose backup"), true);
+ setTitle(Localization.lang("Choose backup file"));
setHeaderText(null);
getDialogPane().setMinHeight(180);
+ getDialogPane().setMinWidth(600);
getDialogPane().getButtonTypes().setAll(RESTORE_BACKUP, IGNORE_BACKUP);
Optional backupPathOpt = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
String backupFilename = backupPathOpt.map(Path::getFileName).map(Path::toString).orElse(Localization.lang("File not found"));
- String content = Localization.lang("The :") + "\n" +
- Localization.lang("Here are some backup versions you can revert to");
+ String content = Localization.lang("Here are some backup files you can revert to.");
- // Create a TableView for backups
+ // Builds TableView
TableView backupTableView = new TableView<>();
- // Define columns
- TableColumn dateColumn = new TableColumn<>("Date of Backup");
+
+ TableColumn dateColumn = new TableColumn<>(Localization.lang("Date of Backup"));
dateColumn.setCellValueFactory(cellData -> cellData.getValue().dateProperty());
- TableColumn sizeColumn = new TableColumn<>("Size of Backup");
+ TableColumn sizeColumn = new TableColumn<>(Localization.lang("Size of Backup"));
sizeColumn.setCellValueFactory(cellData -> cellData.getValue().sizeProperty());
- TableColumn entriesColumn = new TableColumn<>("Number of Entries");
+ TableColumn entriesColumn = new TableColumn<>(Localization.lang("Number of Entries"));
entriesColumn.setCellValueFactory(cellData -> cellData.getValue().entriesProperty().asObject());
- // Add columns to the table
backupTableView.getColumns().addAll(dateColumn, sizeColumn, entriesColumn);
+ backupTableView.getSelectionModel().selectFirst();
// Sample data
- ObservableList data = FXCollections.observableArrayList(
- new BackupEntry("2023-11-01", "500 MB", 120),
- new BackupEntry("2023-10-15", "300 MB", 80),
- new BackupEntry("2023-10-01", "250 MB", 60),
- new BackupEntry("2023-11-01", "500 MB", 120),
- new BackupEntry("2023-10-15", "300 MB", 80),
- new BackupEntry("2023-11-01", "500 MB", 120),
- new BackupEntry("2023-10-15", "300 MB", 80),
- new BackupEntry("2023-11-01", "500 MB", 120),
- new BackupEntry("2023-10-15", "300 MB", 80),
- new BackupEntry("2023-11-01", "500 MB", 120),
- new BackupEntry("2023-10-15", "300 MB", 80),
- new BackupEntry("2023-11-01", "500 MB", 120),
- new BackupEntry("2023-10-15", "300 MB", 80),
- new BackupEntry("2023-11-01", "500 MB", 120),
- new BackupEntry("2023-10-15", "300 MB", 80),
- new BackupEntry("2023-11-01", "500 MB", 120),
- new BackupEntry("2023-10-15", "300 MB", 80)
- );
-
+ ObservableList data = FXCollections.observableArrayList();
+ for (int i = 0; i < 20; i++) { // Adjust 20 to however many entries you want
+ data.add(new BackupEntry("2023-11-01", String.valueOf(i) + " MB", i));
+ }
backupTableView.setItems(data);
setContentText(content);
@@ -86,9 +70,16 @@ public BackupChoiceDialog(Path originalPath, Path backupDir, ExternalApplication
// Create a VBox to hold the table and additional content
VBox contentBox = new VBox(10);
contentBox.getChildren().addAll(new Label(content), backupTableView);
-
+ contentBox.setPrefWidth(380);
// Add the VBox to the dialog's content
getDialogPane().setContent(contentBox);
+
+ setResultConverter(dialogButton -> {
+ if (dialogButton == RESTORE_BACKUP) {
+ return (BackupEntry) backupTableView.getSelectionModel().getSelectedItem();
+ }
+ return null;
+ });
}
}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index b02561cd5ef..18009807de8 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -14,6 +14,7 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.autosaveandbackup.BackupManager;
import org.jabref.gui.backup.BackupChoiceDialog;
+import org.jabref.gui.backup.BackupEntry;
import org.jabref.gui.backup.BackupResolverDialog;
import org.jabref.gui.collab.DatabaseChange;
import org.jabref.gui.collab.DatabaseChangeList;
@@ -61,11 +62,17 @@ public static Optional showRestoreBackupDialog(DialogService dialo
BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
return Optional.empty();
} else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
- var actions = showBackupChoiceDialog(
- dialogService,
- preferences.getExternalApplicationsPreferences(),
- originalPath,
- preferences.getFilePreferences().getBackupDirectory());
+ var test = showBackupChoiceDialog(dialogService, originalPath, preferences);
+ if (test.isPresent()) {
+ LOGGER.warn(String.valueOf(test.get().getEntries()));
+ showBackupResolverDialog(
+ dialogService,
+ preferences.getExternalApplicationsPreferences(),
+ originalPath,
+ preferences.getFilePreferences().getBackupDirectory());
+ } else {
+ LOGGER.warn("Empty");
+ }
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
}
@@ -81,12 +88,11 @@ private static Optional showBackupResolverDialog(DialogService dialo
() -> dialogService.showCustomDialogAndWait(new BackupResolverDialog(originalPath, backupDir, externalApplicationsPreferences)));
}
- private static Optional showBackupChoiceDialog(DialogService dialogService,
- ExternalApplicationsPreferences externalApplicationsPreferences,
- Path originalPath,
- Path backupDir) {
+ private static Optional showBackupChoiceDialog(DialogService dialogService,
+ Path originalPath,
+ GuiPreferences preferences) {
return UiTaskExecutor.runInJavaFXThread(
- () -> dialogService.showCustomDialogAndWait(new BackupChoiceDialog(originalPath, backupDir, externalApplicationsPreferences)));
+ () -> dialogService.showCustomDialogAndWait(new BackupChoiceDialog(originalPath, preferences.getFilePreferences().getBackupDirectory(), preferences.getExternalApplicationsPreferences())));
}
private static Optional showReviewBackupDialog(
From b299e5faf2d31b8871451a1167b0a10351b4df18 Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Wed, 13 Nov 2024 04:54:37 +0100
Subject: [PATCH 13/84] Navigation through dialogs implemented
---
.../jabref/gui/backup/BackupChoiceDialog.java | 89 ++++++++++---------
.../gui/backup/BackupChoiceDialogRecord.java | 8 ++
.../jabref/gui/dialogs/BackupUIManager.java | 37 ++++++--
3 files changed, 85 insertions(+), 49 deletions(-)
create mode 100644 src/main/java/org/jabref/gui/backup/BackupChoiceDialogRecord.java
diff --git a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
index 84d45f0aeaa..8049e5888a6 100644
--- a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
+++ b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
@@ -1,7 +1,6 @@
package org.jabref.gui.backup;
import java.nio.file.Path;
-import java.util.Optional;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@@ -9,77 +8,81 @@
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
+import javafx.scene.control.Pagination;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
-import org.jabref.gui.collab.DatabaseChange;
-import org.jabref.gui.frame.ExternalApplicationsPreferences;
import org.jabref.gui.util.BaseDialog;
import org.jabref.logic.l10n.Localization;
-import org.jabref.logic.util.BackupFileType;
-import org.jabref.logic.util.io.BackupFileUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class BackupChoiceDialog extends BaseDialog {
+public class BackupChoiceDialog extends BaseDialog {
public static final ButtonType RESTORE_BACKUP = new ButtonType(Localization.lang("Restore from backup"), ButtonBar.ButtonData.OK_DONE);
public static final ButtonType IGNORE_BACKUP = new ButtonType(Localization.lang("Ignore backup"), ButtonBar.ButtonData.CANCEL_CLOSE);
+ public static final ButtonType REVIEW_BACKUP = new ButtonType(Localization.lang("Review backup"), ButtonBar.ButtonData.LEFT);
- private static final Logger LOGGER = LoggerFactory.getLogger(BackupChoiceDialog.class);
+ private static final int ROWS_PER_PAGE = 10; // Define number of rows per page
@FXML
- private TableView backupTableView;
+ private final TableView backupTableView;
- public BackupChoiceDialog(Path originalPath, Path backupDir, ExternalApplicationsPreferences externalApplicationsPreferences) {
+ public BackupChoiceDialog(Path originalPath, Path backupDir) {
setTitle(Localization.lang("Choose backup file"));
setHeaderText(null);
- getDialogPane().setMinHeight(180);
- getDialogPane().setMinWidth(600);
- getDialogPane().getButtonTypes().setAll(RESTORE_BACKUP, IGNORE_BACKUP);
-
- Optional backupPathOpt = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
- String backupFilename = backupPathOpt.map(Path::getFileName).map(Path::toString).orElse(Localization.lang("File not found"));
- String content = Localization.lang("Here are some backup files you can revert to.");
-
- // Builds TableView
- TableView backupTableView = new TableView<>();
-
- TableColumn dateColumn = new TableColumn<>(Localization.lang("Date of Backup"));
- dateColumn.setCellValueFactory(cellData -> cellData.getValue().dateProperty());
+ getDialogPane().setMinHeight(150);
+ getDialogPane().setMinWidth(450);
+ getDialogPane().getButtonTypes().setAll(RESTORE_BACKUP, IGNORE_BACKUP, REVIEW_BACKUP);
+ String content = Localization.lang("It looks like JabRef did not shut down cleanly last time the file was used.") + "\n\n" +
+ Localization.lang("Do you want to recover the library from a backup file?");
- TableColumn sizeColumn = new TableColumn<>(Localization.lang("Size of Backup"));
- sizeColumn.setCellValueFactory(cellData -> cellData.getValue().sizeProperty());
-
- TableColumn entriesColumn = new TableColumn<>(Localization.lang("Number of Entries"));
- entriesColumn.setCellValueFactory(cellData -> cellData.getValue().entriesProperty().asObject());
-
- backupTableView.getColumns().addAll(dateColumn, sizeColumn, entriesColumn);
- backupTableView.getSelectionModel().selectFirst();
+ backupTableView = new TableView();
+ setupBackupTableView();
// Sample data
ObservableList data = FXCollections.observableArrayList();
- for (int i = 0; i < 20; i++) { // Adjust 20 to however many entries you want
- data.add(new BackupEntry("2023-11-01", String.valueOf(i) + " MB", i));
+ for (int i = 0; i < 100; i++) { // Adjust 20 to however many entries you want
+ data.add(new BackupEntry("2023-11-01", i + " MB", i));
}
- backupTableView.setItems(data);
-
setContentText(content);
- // Create a VBox to hold the table and additional content
+ // Pagination control
+ int pageCount = (int) Math.ceil(data.size() / (double) ROWS_PER_PAGE);
+ Pagination pagination = new Pagination(pageCount, 0);
+ pagination.setPageFactory(pageIndex -> {
+ int start = pageIndex * ROWS_PER_PAGE;
+ int end = Math.min(start + ROWS_PER_PAGE, data.size());
+ backupTableView.setItems(FXCollections.observableArrayList(data.subList(start, end)));
+ backupTableView.getSelectionModel().selectFirst();
+ return new VBox(backupTableView);
+ });
+
+ // VBox content to hold the pagination and the label
VBox contentBox = new VBox(10);
- contentBox.getChildren().addAll(new Label(content), backupTableView);
+ contentBox.getChildren().addAll(new Label(content), pagination);
contentBox.setPrefWidth(380);
- // Add the VBox to the dialog's content
+
+ // Set the dialog content
getDialogPane().setContent(contentBox);
setResultConverter(dialogButton -> {
- if (dialogButton == RESTORE_BACKUP) {
- return (BackupEntry) backupTableView.getSelectionModel().getSelectedItem();
+ if (dialogButton == RESTORE_BACKUP || dialogButton == REVIEW_BACKUP) {
+ return new BackupChoiceDialogRecord(backupTableView.getSelectionModel().getSelectedItem(), dialogButton);
}
- return null;
+ return new BackupChoiceDialogRecord(null, dialogButton);
});
}
+
+ private void setupBackupTableView() {
+ TableColumn dateColumn = new TableColumn<>(Localization.lang("Date of Backup"));
+ dateColumn.setCellValueFactory(cellData -> cellData.getValue().dateProperty());
+
+ TableColumn sizeColumn = new TableColumn<>(Localization.lang("Size of Backup"));
+ sizeColumn.setCellValueFactory(cellData -> cellData.getValue().sizeProperty());
+
+ TableColumn entriesColumn = new TableColumn<>(Localization.lang("Number of Entries"));
+ entriesColumn.setCellValueFactory(cellData -> cellData.getValue().entriesProperty().asObject());
+
+ backupTableView.getColumns().addAll(dateColumn, sizeColumn, entriesColumn);
+ }
}
diff --git a/src/main/java/org/jabref/gui/backup/BackupChoiceDialogRecord.java b/src/main/java/org/jabref/gui/backup/BackupChoiceDialogRecord.java
new file mode 100644
index 00000000000..0e3cf772825
--- /dev/null
+++ b/src/main/java/org/jabref/gui/backup/BackupChoiceDialogRecord.java
@@ -0,0 +1,8 @@
+package org.jabref.gui.backup;
+
+import javafx.scene.control.ButtonType;
+
+public record BackupChoiceDialogRecord(
+ BackupEntry entry,
+ ButtonType action) {
+}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 18009807de8..9f5be274700 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -14,7 +14,7 @@
import org.jabref.gui.StateManager;
import org.jabref.gui.autosaveandbackup.BackupManager;
import org.jabref.gui.backup.BackupChoiceDialog;
-import org.jabref.gui.backup.BackupEntry;
+import org.jabref.gui.backup.BackupChoiceDialogRecord;
import org.jabref.gui.backup.BackupResolverDialog;
import org.jabref.gui.collab.DatabaseChange;
import org.jabref.gui.collab.DatabaseChangeList;
@@ -57,6 +57,31 @@ public static Optional showRestoreBackupDialog(DialogService dialo
preferences.getExternalApplicationsPreferences(),
originalPath,
preferences.getFilePreferences().getBackupDirectory());
+ return actionOpt.flatMap(action -> {
+ if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
+ BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
+ return Optional.empty();
+ } else if (action == BackupResolverDialog.REVIEW_BACKUP) {
+ return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
+ } else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
+ var recordBackupChoice = showBackupChoiceDialog(dialogService, originalPath, preferences);
+ if (recordBackupChoice.isEmpty()) {
+ return Optional.empty();
+ }
+ if (recordBackupChoice.get().action() == BackupChoiceDialog.RESTORE_BACKUP) {
+ LOGGER.warn(recordBackupChoice.get().entry().getSize());
+ BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
+ return Optional.empty();
+ }
+ if (recordBackupChoice.get().action() == BackupChoiceDialog.REVIEW_BACKUP) {
+ LOGGER.warn(recordBackupChoice.get().entry().getSize());
+ return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
+ }
+ }
+ return Optional.empty();
+ });
+ }
+ /*
return actionOpt.flatMap(action -> {
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
@@ -78,8 +103,8 @@ public static Optional showRestoreBackupDialog(DialogService dialo
}
return Optional.empty();
});
- }
+ */
private static Optional showBackupResolverDialog(DialogService dialogService,
ExternalApplicationsPreferences externalApplicationsPreferences,
Path originalPath,
@@ -88,11 +113,11 @@ private static Optional showBackupResolverDialog(DialogService dialo
() -> dialogService.showCustomDialogAndWait(new BackupResolverDialog(originalPath, backupDir, externalApplicationsPreferences)));
}
- private static Optional showBackupChoiceDialog(DialogService dialogService,
- Path originalPath,
- GuiPreferences preferences) {
+ private static Optional showBackupChoiceDialog(DialogService dialogService,
+ Path originalPath,
+ GuiPreferences preferences) {
return UiTaskExecutor.runInJavaFXThread(
- () -> dialogService.showCustomDialogAndWait(new BackupChoiceDialog(originalPath, preferences.getFilePreferences().getBackupDirectory(), preferences.getExternalApplicationsPreferences())));
+ () -> dialogService.showCustomDialogAndWait(new BackupChoiceDialog(originalPath, preferences.getFilePreferences().getBackupDirectory())));
}
private static Optional showReviewBackupDialog(
From d2b3619f6f606fbd866035ecbea805e59522cac4 Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Tue, 19 Nov 2024 20:44:12 +0100
Subject: [PATCH 14/84] some modif
---
.../autosaveandbackup/BackupManagerJGit.java | 91 +++++++++----------
1 file changed, 42 insertions(+), 49 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index 6ec5a709637..2e4c50fd672 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -6,7 +6,6 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -24,10 +23,12 @@
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -91,7 +92,7 @@ private void startBackupTaskJGit(Path backupDir) {
executor.scheduleAtFixedRate(
() -> {
try {
- performBackup(backupDir);
+ performBackup(backupDir,originalPath);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
}
@@ -101,11 +102,12 @@ private void startBackupTaskJGit(Path backupDir) {
TimeUnit.SECONDS);
}
- private void performBackup(Path backupDir) throws IOException, GitAPIException {
+ private void performBackup(Path backupDir,Path originalPath) throws IOException, GitAPIException {
/*
il faut initialiser needsBackup
*/
+ needsBackup=BackupManagerJGit.backupGitDiffers(backupDir,originalPath);
if (!needsBackup) {
return;
}
@@ -124,7 +126,7 @@ public static void restoreBackupJGit(Path originalPath, Path backupDir, ObjectId
Git git = Git.open(backupDir.toFile());
// Extraire le contenu de l'objet spécifié (commit) dans le répertoire de travail
- git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
+ git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
// Ajouter les modifications au staging
git.add().addFilepattern(".").call();
@@ -138,20 +140,6 @@ public static void restoreBackupJGit(Path originalPath, Path backupDir, ObjectId
}
}
- public static void restoreBackupj(Path originalPath, Path backupDir, ObjectId objectId) {
- try {
-
- Git git = Git.open(backupDir.toFile());
- git.checkout().setName(objectId.getName()).call();
- /*
- faut une methode pour evite le branch nouveau
-
- */
- LOGGER.info("Restored backup from Git repository");
- } catch (IOException | GitAPIException e) {
- LOGGER.error("Error while restoring the backup", e);
- }
- }
/*
compare what is in originalPath and last commit
@@ -167,7 +155,7 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
ObjectId headCommitId = repository.resolve("HEAD"); // to get the latest commit id
if (headCommitId == null) {
// No commits in the repository, so there's no previous backup
- return true;
+ return false;
}
git.add().addFilepattern(originalPath.getFileName().toString()).call();
String relativePath = backupDir.relativize(originalPath).toString();
@@ -213,7 +201,7 @@ public List retreiveCommits(Path backupDir, int n) throws IOException
int count = 0;
int startIndex = n * 10;
- int endIndex = startIndex + 10;
+ int endIndex = startIndex + 9;
for (RevCommit commit : revWalk) {
// Ignorer les commits jusqu'à l'index de départ
@@ -222,7 +210,7 @@ public List retreiveCommits(Path backupDir, int n) throws IOException
continue;
}
// Arrêter lorsque nous avons atteint l'index de fin
- if (count >= endIndex) {
+ if (count > endIndex) {
break;
}
// Ajouter les commits à la liste principale
@@ -230,45 +218,50 @@ public List retreiveCommits(Path backupDir, int n) throws IOException
count++;
}
}
+ }
return retrievedCommits;
}
- public List> retrieveCommitDetails(List commits, Repository repository) throws IOException, GitAPIException {
- List> commitDetails = new ArrayList<>();
+ public List> retrieveCommitDetails(List Commits, Path backupDir) throws IOException, GitAPIException
+ {
- // Parcourir la liste des commits fournie en paramètre
- for (RevCommit commit : commits) {
- // Liste pour stocker les détails du commit
- List commitInfo = new ArrayList<>();
- commitInfo.add(commit.getName()); // ID du commit
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
+ List> commitDetails = new ArrayList<>();
- // Récupérer la taille des fichiers modifiés par le commit
- try (TreeWalk treeWalk = new TreeWalk(repository)) {
- treeWalk.addTree(commit.getTree());
- treeWalk.setRecursive(true);
- long totalSize = 0;
+ // Parcourir la liste des commits fournie en paramètre
+ for (RevCommit commit : Commits) {
+ // Liste pour stocker les détails du commit
+ List commitInfo = new ArrayList<>();
+ commitInfo.add(commit.getName()); // ID du commit
- while (treeWalk.next()) {
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- totalSize += loader.getSize(); // Calculer la taille en octets
- }
+ // Récupérer la taille des fichiers modifiés par le commit
+ try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.addTree(commit.getTree());
+ treeWalk.setRecursive(true);
+ long totalSize = 0;
- // Convertir la taille en Ko ou Mo
- String sizeFormatted = (totalSize > 1024 * 1024)
- ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
- : String.format("%.2f Ko", totalSize / 1024.0);
+ while (treeWalk.next()) {
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ totalSize += loader.getSize(); // Calculer la taille en octets
+ }
- commitInfo.add(sizeFormatted); // Ajouter la taille formatée
- }
+ // Convertir la taille en Ko ou Mo
+ String sizeFormatted = (totalSize > 1024 * 1024)
+ ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
+ : String.format("%.2f Ko", totalSize / 1024.0);
- // Ajouter la liste des détails à la liste principale
- commitDetails.add(commitInfo);
- }
+ commitInfo.add(sizeFormatted); // Ajouter la taille formatée
+ }
- return commitDetails;
- }
+ // Ajouter la liste des détails à la liste principale
+ commitDetails.add(commitInfo);
+ }
+
+ return commitDetails;
+ }
+ }
@@ -289,7 +282,7 @@ private void shutdownJGit(Path backupDir, boolean createBackup) {
if (createBackup) {
try {
- performBackup(backupDir);
+ performBackup(backupDir,originalPath);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during shutdown backup", e);
}
From 54fa60a3429538997bcfd3bcf434edc83769a58d Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Tue, 19 Nov 2024 21:13:58 +0100
Subject: [PATCH 15/84] fix some issues
---
.../autosaveandbackup/BackupManagerJGit.java | 167 +++++++-----------
1 file changed, 59 insertions(+), 108 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index 1e9ddf0fffe..fdf77df33d3 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -49,7 +49,6 @@ public class BackupManagerJGit {
private final LibraryTab libraryTab;
private final Git git;
-
private boolean needsBackup = false;
BackupManagerJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
@@ -76,24 +75,28 @@ public class BackupManagerJGit {
}
}
- public static BackupManagerJGit startJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ public BackupManagerJGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
BackupManagerJGit backupManagerJGit = new BackupManagerJGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManagerJGit.startBackupTaskJGit(preferences.getFilePreferences().getBackupDirectory());
+ backupManagerJGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
runningInstances.add(backupManagerJGit);
return backupManagerJGit;
}
- public static void shutdownJGit(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup));
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup,Path originalPath) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup , originalPath));
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
}
- private void startBackupTaskJGit(Path backupDir) {
+ @SuppressWarnings({"checkstyle:WhitespaceAfter", "checkstyle:LeftCurly"})
+ private void startBackupTask(Path backupDir, Path originalPath)
+ {
executor.scheduleAtFixedRate(
() -> {
try {
- performBackup(backupDir,originalPath);
- } catch (IOException | GitAPIException e) {
+ performBackup(backupDir, originalPath);
+ } catch (
+ IOException |
+ GitAPIException e) {
LOGGER.error("Error during backup", e);
}
},
@@ -102,11 +105,11 @@ private void startBackupTaskJGit(Path backupDir) {
TimeUnit.SECONDS);
}
- private void performBackup(Path backupDir,Path originalPath) throws IOException, GitAPIException {
+ private void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
/*
needsBackup must be initialized
*/
- needsBackup=BackupManagerJGit.backupGitDiffers(backupDir,originalPath);
+ needsBackup = BackupManagerJGit.backupGitDiffers(backupDir, originalPath);
if (!needsBackup) {
return;
}
@@ -120,18 +123,12 @@ private void performBackup(Path backupDir,Path originalPath) throws IOException,
this.needsBackup = false;
}
- public static void restoreBackupJGit(Path originalPath, Path backupDir, ObjectId objectId) {
+ @SuppressWarnings("checkstyle:TodoComment")
+ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
try {
Git git = Git.open(backupDir.toFile());
-<<<<<<< HEAD
- // Extraire le contenu de l'objet spécifié (commit) dans le répertoire de travail
- git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
-=======
- //Extract the content of the object (commit) in the work repository
git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
->>>>>>> b82682aa2e4f9dc82f5abbe95bb5858c02a58d32
-
// Add commits to staging Area
git.add().addFilepattern(".").call();
@@ -139,13 +136,13 @@ public static void restoreBackupJGit(Path originalPath, Path backupDir, ObjectId
git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
LOGGER.info("Restored backup from Git repository and committed the changes");
- } catch (IOException | GitAPIException e) {
+ } catch (
+ IOException |
+ GitAPIException e) {
LOGGER.error("Error while restoring the backup", e);
}
}
-<<<<<<< HEAD
-=======
public static void restoreBackupj(Path originalPath, Path backupDir, ObjectId objectId) {
try {
@@ -156,12 +153,12 @@ public static void restoreBackupj(Path originalPath, Path backupDir, ObjectId ob
need a method to avoid the new branch
*/
LOGGER.info("Restored backup from Git repository");
- } catch (IOException | GitAPIException e) {
+ } catch (
+ IOException |
+ GitAPIException e) {
LOGGER.error("Error while restoring the backup", e);
}
}
->>>>>>> b82682aa2e4f9dc82f5abbe95bb5858c02a58d32
-
/*
compare what is in originalPath and last commit
*/
@@ -187,7 +184,7 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
}
}
- public List showDiffersJGit(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
+ public List showDiffers(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
@@ -206,8 +203,7 @@ need a class to show the last ten backups indicating: date/ size/ number of entr
return diffFr.scan(oldCommit, newCommit);
}
-
-// n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
+ // n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
// and decrements by 1 when the user asks to see the pack of the 10 earlier versions
// the scroll down: n->n+1 ; the scroll up: n->n-1
public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
@@ -230,13 +226,7 @@ public List retreiveCommits(Path backupDir, int n) throws IOException
count++;
continue;
}
-<<<<<<< HEAD
- // Arrêter lorsque nous avons atteint l'index de fin
- if (count > endIndex) {
-=======
- // Stop at endIndex
if (count >= endIndex) {
->>>>>>> b82682aa2e4f9dc82f5abbe95bb5858c02a58d32
break;
}
// Add commits to the main list
@@ -246,92 +236,47 @@ public List retreiveCommits(Path backupDir, int n) throws IOException
}
}
-
return retrievedCommits;
}
- public List> retrieveCommitDetails(List Commits, Path backupDir) throws IOException, GitAPIException
- {
-
-<<<<<<< HEAD
- try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- List> commitDetails = new ArrayList<>();
-
- // Parcourir la liste des commits fournie en paramètre
- for (RevCommit commit : Commits) {
- // Liste pour stocker les détails du commit
- List commitInfo = new ArrayList<>();
- commitInfo.add(commit.getName()); // ID du commit
-
- // Récupérer la taille des fichiers modifiés par le commit
- try (TreeWalk treeWalk = new TreeWalk(repository)) {
- treeWalk.addTree(commit.getTree());
- treeWalk.setRecursive(true);
- long totalSize = 0;
-
- while (treeWalk.next()) {
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- totalSize += loader.getSize(); // Calculer la taille en octets
- }
-
- // Convertir la taille en Ko ou Mo
- String sizeFormatted = (totalSize > 1024 * 1024)
- ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
- : String.format("%.2f Ko", totalSize / 1024.0);
-
- commitInfo.add(sizeFormatted); // Ajouter la taille formatée
+ public List> retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
+ List> commitDetails;
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
+ commitDetails = new ArrayList<>();
+
+ // Browse the list of commits given as a parameter
+ for (RevCommit commit : commits) {
+ // A list to stock details about the commit
+ List commitInfo = new ArrayList<>();
+ commitInfo.add(commit.getName()); // ID of commit
+
+ // Get the size of files changes by the commit
+ try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.addTree(commit.getTree());
+ treeWalk.setRecursive(true);
+ long totalSize = 0;
+
+ while (treeWalk.next()) {
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ totalSize += loader.getSize(); // size in bytes
}
- // Ajouter la liste des détails à la liste principale
- commitDetails.add(commitInfo);
- }
+ // Convert the size to Kb or Mb
+ String sizeFormatted = (totalSize > 1024 * 1024)
+ ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
+ : String.format("%.2f Ko", totalSize / 1024.0);
- return commitDetails;
- }
-=======
- // Browse the list of commits given as a parameter
- for (RevCommit commit : commits) {
- // A list to stock details about the commit
- List commitInfo = new ArrayList<>();
- commitInfo.add(commit.getName()); // ID of commit
-
- // Get the size of files changes by the commit
- try (TreeWalk treeWalk = new TreeWalk(repository)) {
- treeWalk.addTree(commit.getTree());
- treeWalk.setRecursive(true);
- long totalSize = 0;
-
- while (treeWalk.next()) {
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- totalSize += loader.getSize(); // size in bytes
+ commitInfo.add(sizeFormatted); // Add Formatted size
}
- // Convert the size to Kb or Mb
- String sizeFormatted = (totalSize > 1024 * 1024)
- ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
- : String.format("%.2f Ko", totalSize / 1024.0);
-
- commitInfo.add(sizeFormatted); // Add Formatted size
+ // Add list of details to the main list
+ commitDetails.add(commitInfo);
}
-
- // Add list of details to the main list
- commitDetails.add(commitInfo);
->>>>>>> b82682aa2e4f9dc82f5abbe95bb5858c02a58d32
}
-
-
-
- /*
-
- faire une methode qui accepte commit id et retourne les diff differences avec la version actuelle( fait)
- methode qui renvoie n derniers indice de commit
- methode ayant idcommit retourne data
-
- */
-
-
- private void shutdownJGit(Path backupDir, boolean createBackup) {
+ return commitDetails;
+ }
+ private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPath) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
executor.shutdown();
@@ -344,4 +289,10 @@ private void shutdownJGit(Path backupDir, boolean createBackup) {
}
}
}
+}
+
+
+
+
+
From 9a8e615489f6d9346ec56a0a11a96bddb4e18da0 Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Tue, 19 Nov 2024 21:22:04 +0100
Subject: [PATCH 16/84] adding date details
---
.../org/jabref/gui/autosaveandbackup/BackupManagerJGit.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index fdf77df33d3..062a95e3aa7 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -6,6 +6,7 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -269,6 +270,9 @@ public List> retrieveCommitDetails(List commits, Path ba
commitInfo.add(sizeFormatted); // Add Formatted size
}
+ // adding date detail
+ Date date= commit.getAuthorIdent().getWhen();
+ commitInfo.add(date.toString());
// Add list of details to the main list
commitDetails.add(commitInfo);
}
From 0f9fcb932e92deada32246de1565a5382c6278ab Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Tue, 19 Nov 2024 23:41:21 +0100
Subject: [PATCH 17/84] Revert "Merge branch 'branch-1' of
https://github.com/khola22/jabref into branch-1"
This reverts commit df21a4d479b5842b9572ea6bf756a38ba4360ebd, reversing
changes made to 62a8beb05b66576e69fd9604e1712b36c15d86bb.
---
src/jmh/java/module-info.java | 8 -
src/main/java/module-info.java | 1 -
.../autosaveandbackup/BackupManagerJGit.java | 173 ++----------------
.../jabref/gui/dialogs/BackupUIManager.java | 3 -
4 files changed, 17 insertions(+), 168 deletions(-)
delete mode 100644 src/jmh/java/module-info.java
diff --git a/src/jmh/java/module-info.java b/src/jmh/java/module-info.java
deleted file mode 100644
index 4c0962156dc..00000000000
--- a/src/jmh/java/module-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- *
- */
-/**
- *
- */
-module JabRef {
-}
\ No newline at end of file
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 901beace0e9..f0151b8988e 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -193,6 +193,5 @@
requires mslinks;
requires org.antlr.antlr4.runtime;
requires org.libreoffice.uno;
- requires gradle.api;
// endregion
}
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index 062a95e3aa7..e2d8d3e047d 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -1,12 +1,8 @@
package org.jabref.gui.autosaveandbackup;
import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -22,14 +18,10 @@
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
-import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -76,28 +68,24 @@ public class BackupManagerJGit {
}
}
- public BackupManagerJGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
+ public static BackupManagerJGit startJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
BackupManagerJGit backupManagerJGit = new BackupManagerJGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManagerJGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
+ backupManagerJGit.startBackupTaskJGit(preferences.getFilePreferences().getBackupDirectory());
runningInstances.add(backupManagerJGit);
return backupManagerJGit;
}
- public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup,Path originalPath) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup , originalPath));
+ public static void shutdownJGit(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup));
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
}
- @SuppressWarnings({"checkstyle:WhitespaceAfter", "checkstyle:LeftCurly"})
- private void startBackupTask(Path backupDir, Path originalPath)
- {
+ private void startBackupTaskJGit(Path backupDir) {
executor.scheduleAtFixedRate(
() -> {
try {
- performBackup(backupDir, originalPath);
- } catch (
- IOException |
- GitAPIException e) {
+ performBackup(backupDir);
+ } catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
}
},
@@ -106,11 +94,11 @@ private void startBackupTask(Path backupDir, Path originalPath)
TimeUnit.SECONDS);
}
- private void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
+ private void performBackup(Path backupDir) throws IOException, GitAPIException {
/*
- needsBackup must be initialized
+
+ il faut initialiser needsBackup
*/
- needsBackup = BackupManagerJGit.backupGitDiffers(backupDir, originalPath);
if (!needsBackup) {
return;
}
@@ -124,42 +112,16 @@ private void performBackup(Path backupDir, Path originalPath) throws IOException
this.needsBackup = false;
}
- @SuppressWarnings("checkstyle:TodoComment")
- public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
+ public static void restoreBackup(Path originalPath, Path backupDir) {
try {
Git git = Git.open(backupDir.toFile());
-
- git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
- // Add commits to staging Area
- git.add().addFilepattern(".").call();
-
- // Commit with a message
- git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
-
- LOGGER.info("Restored backup from Git repository and committed the changes");
- } catch (
- IOException |
- GitAPIException e) {
- LOGGER.error("Error while restoring the backup", e);
- }
- }
-
- public static void restoreBackupj(Path originalPath, Path backupDir, ObjectId objectId) {
- try {
-
- Git git = Git.open(backupDir.toFile());
- git.checkout().setName(objectId.getName()).call();
- /*
- faut une methode pour evite le branch nouveau
- need a method to avoid the new branch
- */
+ git.checkout().setName("HEAD").call();
LOGGER.info("Restored backup from Git repository");
- } catch (
- IOException |
- GitAPIException e) {
+ } catch (IOException | GitAPIException e) {
LOGGER.error("Error while restoring the backup", e);
}
}
+
/*
compare what is in originalPath and last commit
*/
@@ -174,7 +136,7 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
ObjectId headCommitId = repository.resolve("HEAD"); // to get the latest commit id
if (headCommitId == null) {
// No commits in the repository, so there's no previous backup
- return false;
+ return true;
}
git.add().addFilepattern(originalPath.getFileName().toString()).call();
String relativePath = backupDir.relativize(originalPath).toString();
@@ -185,118 +147,17 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
}
}
- public List showDiffers(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
-
- File repoDir = backupDir.toFile();
- Repository repository = new FileRepositoryBuilder()
- .setGitDir(new File(repoDir, ".git"))
- .build();
- /*
- need a class to show the last ten backups indicating: date/ size/ number of entries
- */
-
- ObjectId oldCommit = repository.resolve(CommitId);
- ObjectId newCommit = repository.resolve("HEAD");
-
- FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
- DiffFormatter diffFr = new DiffFormatter(fos);
- diffFr.setRepository(repository);
- return diffFr.scan(oldCommit, newCommit);
- }
-
- // n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
-// and decrements by 1 when the user asks to see the pack of the 10 earlier versions
-// the scroll down: n->n+1 ; the scroll up: n->n-1
- public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
- List retrievedCommits = new ArrayList<>();
- // Open Git depository
- try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- // Use RevWalk to go through all commits
- try (RevWalk revWalk = new RevWalk(repository)) {
- // Start from HEAD
- RevCommit startCommit = revWalk.parseCommit(repository.resolve("HEAD"));
- revWalk.markStart(startCommit);
-
- int count = 0;
- int startIndex = n * 10;
- int endIndex = startIndex + 9;
-
- for (RevCommit commit : revWalk) {
- // Ignore commits before starting index
- if (count < startIndex) {
- count++;
- continue;
- }
- if (count >= endIndex) {
- break;
- }
- // Add commits to the main list
- retrievedCommits.add(commit);
- count++;
- }
- }
- }
-
- return retrievedCommits;
- }
-
- public List> retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
- List> commitDetails;
- try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- commitDetails = new ArrayList<>();
-
- // Browse the list of commits given as a parameter
- for (RevCommit commit : commits) {
- // A list to stock details about the commit
- List commitInfo = new ArrayList<>();
- commitInfo.add(commit.getName()); // ID of commit
-
- // Get the size of files changes by the commit
- try (TreeWalk treeWalk = new TreeWalk(repository)) {
- treeWalk.addTree(commit.getTree());
- treeWalk.setRecursive(true);
- long totalSize = 0;
-
- while (treeWalk.next()) {
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- totalSize += loader.getSize(); // size in bytes
- }
-
- // Convert the size to Kb or Mb
- String sizeFormatted = (totalSize > 1024 * 1024)
- ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
- : String.format("%.2f Ko", totalSize / 1024.0);
-
- commitInfo.add(sizeFormatted); // Add Formatted size
- }
-
- // adding date detail
- Date date= commit.getAuthorIdent().getWhen();
- commitInfo.add(date.toString());
- // Add list of details to the main list
- commitDetails.add(commitInfo);
- }
- }
-
- return commitDetails;
- }
- private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPath) {
+ private void shutdownJGit(Path backupDir, boolean createBackup) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
executor.shutdown();
if (createBackup) {
try {
- performBackup(backupDir,originalPath);
+ performBackup(backupDir);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during shutdown backup", e);
}
}
}
}
-
-
-
-
-
-
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index d5748d7e5fe..9f5be274700 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -24,7 +24,6 @@
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.util.UiTaskExecutor;
-import org.eclipse.jgit.lib.ObjectId;
import org.jabref.logic.importer.ImportFormatPreferences;
import org.jabref.logic.importer.OpenDatabase;
import org.jabref.logic.importer.ParserResult;
@@ -35,7 +34,6 @@
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.jabref.model.util.FileUpdateMonitor;
-import org.gradle.internal.impldep.org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,7 +59,6 @@ public static Optional showRestoreBackupDialog(DialogService dialo
preferences.getFilePreferences().getBackupDirectory());
return actionOpt.flatMap(action -> {
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
-
BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
return Optional.empty();
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
From 7b6ee3f22ec660b3a85cc7934ea6692500df149d Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Wed, 20 Nov 2024 00:27:11 +0100
Subject: [PATCH 18/84] Reapply "Merge branch 'branch-1' of
https://github.com/khola22/jabref into branch-1"
This reverts commit 0f9fcb932e92deada32246de1565a5382c6278ab.
---
src/jmh/java/module-info.java | 8 +
src/main/java/module-info.java | 1 +
.../autosaveandbackup/BackupManagerJGit.java | 173 ++++++++++++++++--
.../jabref/gui/dialogs/BackupUIManager.java | 3 +
4 files changed, 168 insertions(+), 17 deletions(-)
create mode 100644 src/jmh/java/module-info.java
diff --git a/src/jmh/java/module-info.java b/src/jmh/java/module-info.java
new file mode 100644
index 00000000000..4c0962156dc
--- /dev/null
+++ b/src/jmh/java/module-info.java
@@ -0,0 +1,8 @@
+/**
+ *
+ */
+/**
+ *
+ */
+module JabRef {
+}
\ No newline at end of file
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index f0151b8988e..901beace0e9 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -193,5 +193,6 @@
requires mslinks;
requires org.antlr.antlr4.runtime;
requires org.libreoffice.uno;
+ requires gradle.api;
// endregion
}
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index e2d8d3e047d..062a95e3aa7 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -1,8 +1,12 @@
package org.jabref.gui.autosaveandbackup;
import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -18,10 +22,14 @@
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -68,24 +76,28 @@ public class BackupManagerJGit {
}
}
- public static BackupManagerJGit startJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ public BackupManagerJGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
BackupManagerJGit backupManagerJGit = new BackupManagerJGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManagerJGit.startBackupTaskJGit(preferences.getFilePreferences().getBackupDirectory());
+ backupManagerJGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
runningInstances.add(backupManagerJGit);
return backupManagerJGit;
}
- public static void shutdownJGit(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup));
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup,Path originalPath) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup , originalPath));
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
}
- private void startBackupTaskJGit(Path backupDir) {
+ @SuppressWarnings({"checkstyle:WhitespaceAfter", "checkstyle:LeftCurly"})
+ private void startBackupTask(Path backupDir, Path originalPath)
+ {
executor.scheduleAtFixedRate(
() -> {
try {
- performBackup(backupDir);
- } catch (IOException | GitAPIException e) {
+ performBackup(backupDir, originalPath);
+ } catch (
+ IOException |
+ GitAPIException e) {
LOGGER.error("Error during backup", e);
}
},
@@ -94,11 +106,11 @@ private void startBackupTaskJGit(Path backupDir) {
TimeUnit.SECONDS);
}
- private void performBackup(Path backupDir) throws IOException, GitAPIException {
+ private void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
/*
-
- il faut initialiser needsBackup
+ needsBackup must be initialized
*/
+ needsBackup = BackupManagerJGit.backupGitDiffers(backupDir, originalPath);
if (!needsBackup) {
return;
}
@@ -112,16 +124,42 @@ private void performBackup(Path backupDir) throws IOException, GitAPIException {
this.needsBackup = false;
}
- public static void restoreBackup(Path originalPath, Path backupDir) {
+ @SuppressWarnings("checkstyle:TodoComment")
+ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
try {
Git git = Git.open(backupDir.toFile());
- git.checkout().setName("HEAD").call();
- LOGGER.info("Restored backup from Git repository");
- } catch (IOException | GitAPIException e) {
+
+ git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
+ // Add commits to staging Area
+ git.add().addFilepattern(".").call();
+
+ // Commit with a message
+ git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
+
+ LOGGER.info("Restored backup from Git repository and committed the changes");
+ } catch (
+ IOException |
+ GitAPIException e) {
LOGGER.error("Error while restoring the backup", e);
}
}
+ public static void restoreBackupj(Path originalPath, Path backupDir, ObjectId objectId) {
+ try {
+
+ Git git = Git.open(backupDir.toFile());
+ git.checkout().setName(objectId.getName()).call();
+ /*
+ faut une methode pour evite le branch nouveau
+ need a method to avoid the new branch
+ */
+ LOGGER.info("Restored backup from Git repository");
+ } catch (
+ IOException |
+ GitAPIException e) {
+ LOGGER.error("Error while restoring the backup", e);
+ }
+ }
/*
compare what is in originalPath and last commit
*/
@@ -136,7 +174,7 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
ObjectId headCommitId = repository.resolve("HEAD"); // to get the latest commit id
if (headCommitId == null) {
// No commits in the repository, so there's no previous backup
- return true;
+ return false;
}
git.add().addFilepattern(originalPath.getFileName().toString()).call();
String relativePath = backupDir.relativize(originalPath).toString();
@@ -147,17 +185,118 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
}
}
- private void shutdownJGit(Path backupDir, boolean createBackup) {
+ public List showDiffers(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
+
+ File repoDir = backupDir.toFile();
+ Repository repository = new FileRepositoryBuilder()
+ .setGitDir(new File(repoDir, ".git"))
+ .build();
+ /*
+ need a class to show the last ten backups indicating: date/ size/ number of entries
+ */
+
+ ObjectId oldCommit = repository.resolve(CommitId);
+ ObjectId newCommit = repository.resolve("HEAD");
+
+ FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
+ DiffFormatter diffFr = new DiffFormatter(fos);
+ diffFr.setRepository(repository);
+ return diffFr.scan(oldCommit, newCommit);
+ }
+
+ // n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
+// and decrements by 1 when the user asks to see the pack of the 10 earlier versions
+// the scroll down: n->n+1 ; the scroll up: n->n-1
+ public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
+ List retrievedCommits = new ArrayList<>();
+ // Open Git depository
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
+ // Use RevWalk to go through all commits
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ // Start from HEAD
+ RevCommit startCommit = revWalk.parseCommit(repository.resolve("HEAD"));
+ revWalk.markStart(startCommit);
+
+ int count = 0;
+ int startIndex = n * 10;
+ int endIndex = startIndex + 9;
+
+ for (RevCommit commit : revWalk) {
+ // Ignore commits before starting index
+ if (count < startIndex) {
+ count++;
+ continue;
+ }
+ if (count >= endIndex) {
+ break;
+ }
+ // Add commits to the main list
+ retrievedCommits.add(commit);
+ count++;
+ }
+ }
+ }
+
+ return retrievedCommits;
+ }
+
+ public List> retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
+ List> commitDetails;
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
+ commitDetails = new ArrayList<>();
+
+ // Browse the list of commits given as a parameter
+ for (RevCommit commit : commits) {
+ // A list to stock details about the commit
+ List commitInfo = new ArrayList<>();
+ commitInfo.add(commit.getName()); // ID of commit
+
+ // Get the size of files changes by the commit
+ try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.addTree(commit.getTree());
+ treeWalk.setRecursive(true);
+ long totalSize = 0;
+
+ while (treeWalk.next()) {
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ totalSize += loader.getSize(); // size in bytes
+ }
+
+ // Convert the size to Kb or Mb
+ String sizeFormatted = (totalSize > 1024 * 1024)
+ ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
+ : String.format("%.2f Ko", totalSize / 1024.0);
+
+ commitInfo.add(sizeFormatted); // Add Formatted size
+ }
+
+ // adding date detail
+ Date date= commit.getAuthorIdent().getWhen();
+ commitInfo.add(date.toString());
+ // Add list of details to the main list
+ commitDetails.add(commitInfo);
+ }
+ }
+
+ return commitDetails;
+ }
+ private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPath) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
executor.shutdown();
if (createBackup) {
try {
- performBackup(backupDir);
+ performBackup(backupDir,originalPath);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during shutdown backup", e);
}
}
}
}
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 9f5be274700..d5748d7e5fe 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -24,6 +24,7 @@
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.util.UiTaskExecutor;
+import org.eclipse.jgit.lib.ObjectId;
import org.jabref.logic.importer.ImportFormatPreferences;
import org.jabref.logic.importer.OpenDatabase;
import org.jabref.logic.importer.ParserResult;
@@ -34,6 +35,7 @@
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.jabref.model.util.FileUpdateMonitor;
+import org.gradle.internal.impldep.org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -59,6 +61,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
preferences.getFilePreferences().getBackupDirectory());
return actionOpt.flatMap(action -> {
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
+
BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
return Optional.empty();
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
From b67b2d97fa0863db49a82a1af5803fa069d55aba Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Wed, 20 Nov 2024 03:03:13 +0100
Subject: [PATCH 19/84] minor fixes
---
src/main/java/module-info.java | 1 -
src/main/java/org/jabref/gui/dialogs/BackupUIManager.java | 3 ---
2 files changed, 4 deletions(-)
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 901beace0e9..f0151b8988e 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -193,6 +193,5 @@
requires mslinks;
requires org.antlr.antlr4.runtime;
requires org.libreoffice.uno;
- requires gradle.api;
// endregion
}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index d5748d7e5fe..9f5be274700 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -24,7 +24,6 @@
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.util.UiTaskExecutor;
-import org.eclipse.jgit.lib.ObjectId;
import org.jabref.logic.importer.ImportFormatPreferences;
import org.jabref.logic.importer.OpenDatabase;
import org.jabref.logic.importer.ParserResult;
@@ -35,7 +34,6 @@
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.jabref.model.util.FileUpdateMonitor;
-import org.gradle.internal.impldep.org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,7 +59,6 @@ public static Optional showRestoreBackupDialog(DialogService dialo
preferences.getFilePreferences().getBackupDirectory());
return actionOpt.flatMap(action -> {
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
-
BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
return Optional.empty();
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
From 3f9584edb83af0cef8c96364c87322ea7935b778 Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Wed, 20 Nov 2024 11:27:13 +0100
Subject: [PATCH 20/84] Minor Synthax fixes
---
.../gui/autosaveandbackup/BackupManagerJGit.java | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
index 062a95e3aa7..231757acf3b 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
@@ -83,14 +83,13 @@ public BackupManagerJGit start(LibraryTab libraryTab, BibDatabaseContext bibData
return backupManagerJGit;
}
- public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup,Path originalPath) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup , originalPath));
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup, Path originalPath) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup, originalPath));
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
}
@SuppressWarnings({"checkstyle:WhitespaceAfter", "checkstyle:LeftCurly"})
- private void startBackupTask(Path backupDir, Path originalPath)
- {
+ private void startBackupTask(Path backupDir, Path originalPath) {
executor.scheduleAtFixedRate(
() -> {
try {
@@ -271,7 +270,7 @@ public List> retrieveCommitDetails(List commits, Path ba
}
// adding date detail
- Date date= commit.getAuthorIdent().getWhen();
+ Date date = commit.getAuthorIdent().getWhen();
commitInfo.add(date.toString());
// Add list of details to the main list
commitDetails.add(commitInfo);
@@ -280,6 +279,7 @@ public List> retrieveCommitDetails(List commits, Path ba
return commitDetails;
}
+
private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPath) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
@@ -287,7 +287,7 @@ private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPat
if (createBackup) {
try {
- performBackup(backupDir,originalPath);
+ performBackup(backupDir, originalPath);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during shutdown backup", e);
}
From 76b6f92152a84c679f94b3008e67910f957d9a8a Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Wed, 20 Nov 2024 11:32:43 +0100
Subject: [PATCH 21/84] supression de classe BackupManagerJGit
---
.../gui/autosaveandbackup/BackupManager.java | 527 ++++++++----------
.../autosaveandbackup/BackupManagerJGit.java | 302 ----------
.../autosaveandbackup/BackupManagerTest.java | 2 +-
3 files changed, 220 insertions(+), 611 deletions(-)
delete mode 100644 src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
index 849ecbccac7..fe0c16df42e 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
@@ -1,65 +1,46 @@
package org.jabref.gui.autosaveandbackup;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.Writer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.FileTime;
+import java.util.ArrayList;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
-import java.util.Optional;
-import java.util.Queue;
import java.util.Set;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import javafx.scene.control.TableColumn;
-
import org.jabref.gui.LibraryTab;
-import org.jabref.gui.maintable.BibEntryTableViewModel;
-import org.jabref.gui.maintable.columns.MainTableColumn;
-import org.jabref.logic.bibtex.InvalidFieldValueException;
-import org.jabref.logic.exporter.AtomicFileWriter;
-import org.jabref.logic.exporter.BibWriter;
-import org.jabref.logic.exporter.BibtexDatabaseWriter;
-import org.jabref.logic.exporter.SelfContainedSaveConfiguration;
import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.util.BackupFileType;
import org.jabref.logic.util.CoarseChangeFilter;
-import org.jabref.logic.util.io.BackupFileUtil;
-import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.database.event.BibDatabaseContextChangedEvent;
-import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryTypesManager;
-import org.jabref.model.metadata.SaveOrder;
-import org.jabref.model.metadata.SelfContainedSaveOrder;
-import com.google.common.eventbus.Subscribe;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/**
- * Backups the given bib database file from {@link BibDatabaseContext} on every {@link BibDatabaseContextChangedEvent}.
- * An intelligent {@link ExecutorService} with a {@link BlockingQueue} prevents a high load while making backups and
- * rejects all redundant backup tasks. This class does not manage the .bak file which is created when opening a
- * database.
- */
public class BackupManager {
private static final Logger LOGGER = LoggerFactory.getLogger(BackupManager.class);
- private static final int MAXIMUM_BACKUP_FILE_COUNT = 10;
-
private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
- private static Set runningInstances = new HashSet<>();
+ private static Set runningInstances = new HashSet();
private final BibDatabaseContext bibDatabaseContext;
private final CliPreferences preferences;
@@ -67,13 +48,11 @@ public class BackupManager {
private final CoarseChangeFilter changeFilter;
private final BibEntryTypesManager entryTypesManager;
private final LibraryTab libraryTab;
+ private final Git git;
- // Contains a list of all backup paths
- // During writing, the less recent backup file is deleted
- private final Queue backupFilesQueue = new LinkedBlockingQueue<>();
private boolean needsBackup = false;
- BackupManager(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
+ BackupManager(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
this.bibDatabaseContext = bibDatabaseContext;
this.entryTypesManager = entryTypesManager;
this.preferences = preferences;
@@ -82,312 +61,244 @@ public class BackupManager {
changeFilter = new CoarseChangeFilter(bibDatabaseContext);
changeFilter.registerListener(this);
- }
-
- /**
- * Determines the most recent backup file name
- */
- static Path getBackupPathForNewBackup(Path originalPath, Path backupDir) {
- return BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(originalPath, BackupFileType.BACKUP, backupDir);
- }
- /**
- * Determines the most recent existing backup file name
- */
- static Optional getLatestBackupPath(Path originalPath, Path backupDir) {
- return BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
- }
-
- /**
- * Starts the BackupManager which is associated with the given {@link BibDatabaseContext}. As long as no database
- * file is present in {@link BibDatabaseContext}, the {@link BackupManager} will do nothing.
- *
- * This method is not thread-safe. The caller has to ensure that this method is not called in parallel.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- */
- public static BackupManager start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
- BackupManager backupManager = new BackupManager(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManager.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
- runningInstances.add(backupManager);
- return backupManager;
+ // Initialize Git repository
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
+ git = new Git(builder.setGitDir(new File(preferences.getFilePreferences().getBackupDirectory().toFile(), ".git"))
+ .readEnvironment()
+ .findGitDir()
+ .build());
+ if (git.getRepository().getObjectDatabase().exists()) {
+ LOGGER.info("Git repository already exists");
+ } else {
+ git.init().call();
+ LOGGER.info("Initialized new Git repository");
+ }
}
- /**
- * Marks the backup as discarded at the library which is associated with the given {@link BibDatabaseContext}.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- */
- public static void discardBackup(BibDatabaseContext bibDatabaseContext, Path backupDir) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.discardBackup(backupDir));
+ public BackupManager start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
+ BackupManager BackupManager = new BackupManager(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ BackupManager.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
+ runningInstances.add(BackupManager);
+ return BackupManager;
}
- /**
- * Shuts down the BackupManager which is associated with the given {@link BibDatabaseContext}.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- * @param createBackup True, if a backup should be created
- * @param backupDir The path to the backup directory
- */
- public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdown(backupDir, createBackup));
+ @SuppressWarnings({"checkstyle:NoWhitespaceBefore", "checkstyle:WhitespaceAfter"})
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup, Path originalPath) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup, originalPath));
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
}
- /**
- * Checks whether a backup file exists for the given database file. If it exists, it is checked whether it is
- * newer and different from the original.
- *
- * In case a discarded file is present, the method also returns false
, See also {@link #discardBackup(Path)}.
- *
- * @param originalPath Path to the file a backup should be checked for. Example: jabref.bib.
- *
- * @return true
if backup file exists AND differs from originalPath. false
is the
- * "default" return value in the good case. In case a discarded file exists, false
is returned, too.
- * In the case of an exception true
is returned to ensure that the user checks the output.
- */
- public static boolean backupFileDiffers(Path originalPath, Path backupDir) {
- Path discardedFile = determineDiscardedFile(originalPath, backupDir);
- if (Files.exists(discardedFile)) {
- try {
- Files.delete(discardedFile);
- } catch (IOException e) {
- LOGGER.error("Could not remove discarded file {}", discardedFile, e);
- return true;
- }
- return false;
- }
- return getLatestBackupPath(originalPath, backupDir).map(latestBackupPath -> {
- FileTime latestBackupFileLastModifiedTime;
- try {
- latestBackupFileLastModifiedTime = Files.getLastModifiedTime(latestBackupPath);
- } catch (IOException e) {
- LOGGER.debug("Could not get timestamp of backup file {}", latestBackupPath, e);
- // If we cannot get the timestamp, we do show any warning
- return false;
- }
- FileTime currentFileLastModifiedTime;
- try {
- currentFileLastModifiedTime = Files.getLastModifiedTime(originalPath);
- } catch (IOException e) {
- LOGGER.debug("Could not get timestamp of current file file {}", originalPath, e);
- // If we cannot get the timestamp, we do show any warning
- return false;
- }
- if (latestBackupFileLastModifiedTime.compareTo(currentFileLastModifiedTime) <= 0) {
- // Backup is older than current file
- // We treat the backup as non-different (even if it could differ)
- return false;
- }
- try {
- boolean result = Files.mismatch(originalPath, latestBackupPath) != -1L;
- if (result) {
- LOGGER.info("Backup file {} differs from current file {}", latestBackupPath, originalPath);
- }
- return result;
- } catch (IOException e) {
- LOGGER.debug("Could not compare original file and backup file.", e);
- // User has to investigate in this case
- return true;
- }
- }).orElse(false);
+ @SuppressWarnings({"checkstyle:WhitespaceAfter", "checkstyle:LeftCurly"})
+ private void startBackupTask(Path backupDir, Path originalPath) {
+ executor.scheduleAtFixedRate(
+ () -> {
+ try {
+ performBackup(backupDir, originalPath);
+ } catch (
+ IOException |
+ GitAPIException e) {
+ LOGGER.error("Error during backup", e);
+ }
+ },
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ TimeUnit.SECONDS);
}
- /**
- * Restores the backup file by copying and overwriting the original one.
- *
- * @param originalPath Path to the file which should be equalized to the backup file.
- */
- public static void restoreBackup(Path originalPath, Path backupDir) {
- Optional backupPath = getLatestBackupPath(originalPath, backupDir);
- if (backupPath.isEmpty()) {
- LOGGER.error("There is no backup file");
+ private void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
+ /*
+ needsBackup must be initialized
+ */
+ needsBackup = BackupManager.backupGitDiffers(backupDir, originalPath);
+ if (!needsBackup) {
return;
}
+
+ // Add and commit changes
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
+ LOGGER.info("Committed backup: {}", commit.getId());
+
+ // Reset the backup flag
+ this.needsBackup = false;
+ }
+
+ @SuppressWarnings("checkstyle:TodoComment")
+ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
try {
- Files.copy(backupPath.get(), originalPath, StandardCopyOption.REPLACE_EXISTING);
- } catch (IOException e) {
- LOGGER.error("Error while restoring the backup file.", e);
+ Git git = Git.open(backupDir.toFile());
+
+ git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
+ // Add commits to staging Area
+ git.add().addFilepattern(".").call();
+
+ // Commit with a message
+ git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
+
+ LOGGER.info("Restored backup from Git repository and committed the changes");
+ } catch (
+ IOException |
+ GitAPIException e) {
+ LOGGER.error("Error while restoring the backup", e);
}
}
- Optional determineBackupPathForNewBackup(Path backupDir) {
- return bibDatabaseContext.getDatabasePath().map(path -> BackupManager.getBackupPathForNewBackup(path, backupDir));
- }
+ public static void restoreBackupj(Path originalPath, Path backupDir, ObjectId objectId) {
+ try {
- /**
- * This method is called as soon as the scheduler says: "Do the backup"
- *
- * SIDE EFFECT: Deletes oldest backup file
- *
- * @param backupPath the full path to the file where the library should be backed up to
- */
- void performBackup(Path backupPath) {
- if (!needsBackup) {
- return;
+ Git git = Git.open(backupDir.toFile());
+ git.checkout().setName(objectId.getName()).call();
+ /*
+ faut une methode pour evite le branch nouveau
+ need a method to avoid the new branch
+ */
+ LOGGER.info("Restored backup from Git repository");
+ } catch (
+ IOException |
+ GitAPIException e) {
+ LOGGER.error("Error while restoring the backup", e);
}
-
- // We opted for "while" to delete backups in case there are more than 10
- while (backupFilesQueue.size() >= MAXIMUM_BACKUP_FILE_COUNT) {
- Path oldestBackupFile = backupFilesQueue.poll();
- try {
- Files.delete(oldestBackupFile);
- } catch (IOException e) {
- LOGGER.error("Could not delete backup file {}", oldestBackupFile, e);
+ }
+ /*
+ compare what is in originalPath and last commit
+ */
+
+ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
+
+ File repoDir = backupDir.toFile();
+ Repository repository = new FileRepositoryBuilder()
+ .setGitDir(new File(repoDir, ".git"))
+ .build();
+ try (Git git = new Git(repository)) {
+ ObjectId headCommitId = repository.resolve("HEAD"); // to get the latest commit id
+ if (headCommitId == null) {
+ // No commits in the repository, so there's no previous backup
+ return false;
}
- }
- //l'ordre dans lequel les entrées BibTeX doivent être écrites dans le fichier de sauvegarde.
- // Si l'utilisateur a trié la table d'affichage des entrées dans JabRef, cet ordre est récupéré.
- // Sinon, un ordre par défaut est utilisé.
- // code similar to org.jabref.gui.exporter.SaveDatabaseAction.saveDatabase
- SelfContainedSaveOrder saveOrder = bibDatabaseContext
- .getMetaData().getSaveOrder()
- .map(so -> {
- if (so.getOrderType() == SaveOrder.OrderType.TABLE) {
- // We need to "flatten out" SaveOrder.OrderType.TABLE as BibWriter does not have access to preferences
- List> sortOrder = libraryTab.getMainTable().getSortOrder();
- return new SelfContainedSaveOrder(
- SaveOrder.OrderType.SPECIFIED,
- sortOrder.stream()
- .filter(col -> col instanceof MainTableColumn>)
- .map(column -> ((MainTableColumn>) column).getModel())
- .flatMap(model -> model.getSortCriteria().stream())
- .toList());
- } else {
- return SelfContainedSaveOrder.of(so);
- }
- })
- .orElse(SaveOrder.getDefaultSaveOrder());
-
- //Elle configure la sauvegarde, en indiquant qu'aucune sauvegarde supplémentaire (backup) ne doit être créée,
- // que l'ordre de sauvegarde doit être celui défini, et que les entrées doivent être formatées selon les préférences
- // utilisateur.
- SelfContainedSaveConfiguration saveConfiguration = (SelfContainedSaveConfiguration) new SelfContainedSaveConfiguration()
- .withMakeBackup(false)
- .withSaveOrder(saveOrder)
- .withReformatOnSave(preferences.getLibraryPreferences().shouldAlwaysReformatOnSave());
-
- // "Clone" the database context
- // We "know" that "only" the BibEntries might be changed during writing (see [org.jabref.logic.exporter.BibDatabaseWriter.savePartOfDatabase])
- //Chaque entrée BibTeX (comme un article, livre, etc.) est clonée en utilisant la méthode clone().
- // Cela garantit que les modifications faites pendant la sauvegarde n'affecteront pas l'entrée originale.
- List list = bibDatabaseContext.getDatabase().getEntries().stream()
- .map(BibEntry::clone)
- .map(BibEntry.class::cast)
- .toList();
- BibDatabase bibDatabaseClone = new BibDatabase(list);
- BibDatabaseContext bibDatabaseContextClone = new BibDatabaseContext(bibDatabaseClone, bibDatabaseContext.getMetaData());
- //Elle définit l'encodage à utiliser pour écrire le fichier. Cela garantit que les caractères spéciaux sont bien sauvegardés.
- Charset encoding = bibDatabaseContext.getMetaData().getEncoding().orElse(StandardCharsets.UTF_8);
- // We want to have successful backups only
- // Thus, we do not use a plain "FileWriter", but the "AtomicFileWriter"
- // Example: What happens if one hard powers off the machine (or kills the jabref process) during writing of the backup?
- // This MUST NOT create a broken backup file that then jabref wants to "restore" from?
- try (Writer writer = new AtomicFileWriter(backupPath, encoding, false)) {
- BibWriter bibWriter = new BibWriter(writer, bibDatabaseContext.getDatabase().getNewLineSeparator());
- new BibtexDatabaseWriter(
- bibWriter,
- saveConfiguration,
- preferences.getFieldPreferences(),
- preferences.getCitationKeyPatternPreferences(),
- entryTypesManager)
- // we save the clone to prevent the original database (and thus the UI) from being changed
- .saveDatabase(bibDatabaseContextClone);
- backupFilesQueue.add(backupPath);
-
- // We wrote the file successfully
- // Thus, we currently do not need any new backup
- this.needsBackup = false;
- } catch (IOException e) {
- logIfCritical(backupPath, e);
+ git.add().addFilepattern(originalPath.getFileName().toString()).call();
+ String relativePath = backupDir.relativize(originalPath).toString();
+ List diffs = git.diff()
+ .setPathFilter(PathFilter.create(relativePath)) // Utiliser PathFilter ici
+ .call();
+ return !diffs.isEmpty();
}
}
- private static Path determineDiscardedFile(Path file, Path backupDir) {
- return backupDir.resolve(BackupFileUtil.getUniqueFilePrefix(file) + "--" + file.getFileName() + "--discarded");
- }
+ public List showDiffers(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
- /**
- * Marks the backups as discarded.
- *
- * We do not delete any files, because the user might want to recover old backup files.
- * Therefore, we mark discarded backups by a --discarded file.
- */
- public void discardBackup(Path backupDir) {
- Path path = determineDiscardedFile(bibDatabaseContext.getDatabasePath().get(), backupDir);
- try {
- Files.createFile(path);
- } catch (IOException e) {
- LOGGER.info("Could not create backup file {}", path, e);
- }
- }
+ File repoDir = backupDir.toFile();
+ Repository repository = new FileRepositoryBuilder()
+ .setGitDir(new File(repoDir, ".git"))
+ .build();
+ /*
+ need a class to show the last ten backups indicating: date/ size/ number of entries
+ */
- private void logIfCritical(Path backupPath, IOException e) {
- Throwable innermostCause = e;
- while (innermostCause.getCause() != null) {
- innermostCause = innermostCause.getCause();
- }
- boolean isErrorInField = innermostCause instanceof InvalidFieldValueException;
+ ObjectId oldCommit = repository.resolve(CommitId);
+ ObjectId newCommit = repository.resolve("HEAD");
- // do not print errors in field values into the log during autosave
- if (!isErrorInField) {
- LOGGER.error("Error while saving to file {}", backupPath, e);
- }
+ FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
+ DiffFormatter diffFr = new DiffFormatter(fos);
+ diffFr.setRepository(repository);
+ return diffFr.scan(oldCommit, newCommit);
}
- @Subscribe
- public synchronized void listen(@SuppressWarnings("unused") BibDatabaseContextChangedEvent event) {
- if (!event.isFilteredOut()) {
- this.needsBackup = true;
+ // n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
+// and decrements by 1 when the user asks to see the pack of the 10 earlier versions
+// the scroll down: n->n+1 ; the scroll up: n->n-1
+ public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
+ List retrievedCommits = new ArrayList<>();
+ // Open Git depository
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
+ // Use RevWalk to go through all commits
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ // Start from HEAD
+ RevCommit startCommit = revWalk.parseCommit(repository.resolve("HEAD"));
+ revWalk.markStart(startCommit);
+
+ int count = 0;
+ int startIndex = n * 10;
+ int endIndex = startIndex + 9;
+
+ for (RevCommit commit : revWalk) {
+ // Ignore commits before starting index
+ if (count < startIndex) {
+ count++;
+ continue;
+ }
+ if (count >= endIndex) {
+ break;
+ }
+ // Add commits to the main list
+ retrievedCommits.add(commit);
+ count++;
+ }
+ }
}
+
+ return retrievedCommits;
}
- private void startBackupTask(Path backupDir) {
- fillQueue(backupDir);//remplie backupFilesQueue les files .sav de le meme bibl
+ @SuppressWarnings("checkstyle:WhitespaceAround")
+ public List> retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
+ List> commitDetails;
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
+ commitDetails = new ArrayList<>();
+
+ // Browse the list of commits given as a parameter
+ for (RevCommit commit : commits) {
+ // A list to stock details about the commit
+ List commitInfo = new ArrayList<>();
+ commitInfo.add(commit.getName()); // ID of commit
+
+ // Get the size of files changes by the commit
+ try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.addTree(commit.getTree());
+ treeWalk.setRecursive(true);
+ long totalSize = 0;
+
+ while (treeWalk.next()) {
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ totalSize += loader.getSize(); // size in bytes
+ }
+
+ // Convert the size to Kb or Mb
+ String sizeFormatted = (totalSize > 1024 * 1024)
+ ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
+ : String.format("%.2f Ko", totalSize / 1024.0);
- executor.scheduleAtFixedRate(
- // We need to determine the backup path on each action, because we use the timestamp in the filename
- () -> determineBackupPathForNewBackup(backupDir).ifPresent(path -> this.performBackup(path)),
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- TimeUnit.SECONDS);
- }
-//La méthode fillQueue(backupDir) est définie dans le code et son rôle est de lister et d'ajouter
-// les fichiers de sauvegarde existants dans une file d'attente,
- private void fillQueue(Path backupDir) {
- if (!Files.exists(backupDir)) {
- return;
- }
- bibDatabaseContext.getDatabasePath().ifPresent(databasePath -> {
- // code similar to {@link org.jabref.logic.util.io.BackupFileUtil.getPathOfLatestExisingBackupFile}
- final String prefix = BackupFileUtil.getUniqueFilePrefix(databasePath) + "--" + databasePath.getFileName();
- try {
- List allSavFiles = Files.list(backupDir)
- // just list the .sav belonging to the given targetFile
- .filter(p -> p.getFileName().toString().startsWith(prefix))//tous les files .sav commencerait par ce prefix
- .sorted().toList();
- backupFilesQueue.addAll(allSavFiles);
- } catch (IOException e) {
- LOGGER.error("Could not determine most recent file", e);
+ commitInfo.add(sizeFormatted); // Add Formatted size
+ }
+
+ // adding date detail
+ Date date = commit.getAuthorIdent().getWhen();
+ commitInfo.add(date.toString());
+ // Add list of details to the main list
+ commitDetails.add(commitInfo);
}
- });
+ }
+
+ return commitDetails;
}
- /**
- * Unregisters the BackupManager from the eventBus of {@link BibDatabaseContext}.
- * This method should only be used when closing a database/JabRef in a normal way.
- *
- * @param backupDir The backup directory
- * @param createBackup If the backup manager should still perform a backup
- */
- private void shutdown(Path backupDir, boolean createBackup) {
+ private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPath) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
executor.shutdown();
if (createBackup) {
- // Ensure that backup is a recent one
- determineBackupPathForNewBackup(backupDir).ifPresent(this::performBackup);
+ try {
+ performBackup(backupDir, originalPath);
+ } catch (IOException | GitAPIException e) {
+ LOGGER.error("Error during shutdown backup", e);
+ }
}
}
}
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
deleted file mode 100644
index 062a95e3aa7..00000000000
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerJGit.java
+++ /dev/null
@@ -1,302 +0,0 @@
-package org.jabref.gui.autosaveandbackup;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-import org.jabref.gui.LibraryTab;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.util.CoarseChangeFilter;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntryTypesManager;
-
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.diff.DiffEntry;
-import org.eclipse.jgit.diff.DiffFormatter;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.treewalk.filter.PathFilter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class BackupManagerJGit {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(BackupManagerJGit.class);
-
- private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
-
- private static Set runningInstances = new HashSet();
-
- private final BibDatabaseContext bibDatabaseContext;
- private final CliPreferences preferences;
- private final ScheduledThreadPoolExecutor executor;
- private final CoarseChangeFilter changeFilter;
- private final BibEntryTypesManager entryTypesManager;
- private final LibraryTab libraryTab;
- private final Git git;
-
- private boolean needsBackup = false;
-
- BackupManagerJGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
- this.bibDatabaseContext = bibDatabaseContext;
- this.entryTypesManager = entryTypesManager;
- this.preferences = preferences;
- this.executor = new ScheduledThreadPoolExecutor(2);
- this.libraryTab = libraryTab;
-
- changeFilter = new CoarseChangeFilter(bibDatabaseContext);
- changeFilter.registerListener(this);
-
- // Initialize Git repository
- FileRepositoryBuilder builder = new FileRepositoryBuilder();
- git = new Git(builder.setGitDir(new File(preferences.getFilePreferences().getBackupDirectory().toFile(), ".git"))
- .readEnvironment()
- .findGitDir()
- .build());
- if (git.getRepository().getObjectDatabase().exists()) {
- LOGGER.info("Git repository already exists");
- } else {
- git.init().call();
- LOGGER.info("Initialized new Git repository");
- }
- }
-
- public BackupManagerJGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
- BackupManagerJGit backupManagerJGit = new BackupManagerJGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManagerJGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
- runningInstances.add(backupManagerJGit);
- return backupManagerJGit;
- }
-
- public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup,Path originalPath) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup , originalPath));
- runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
- }
-
- @SuppressWarnings({"checkstyle:WhitespaceAfter", "checkstyle:LeftCurly"})
- private void startBackupTask(Path backupDir, Path originalPath)
- {
- executor.scheduleAtFixedRate(
- () -> {
- try {
- performBackup(backupDir, originalPath);
- } catch (
- IOException |
- GitAPIException e) {
- LOGGER.error("Error during backup", e);
- }
- },
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- TimeUnit.SECONDS);
- }
-
- private void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
- /*
- needsBackup must be initialized
- */
- needsBackup = BackupManagerJGit.backupGitDiffers(backupDir, originalPath);
- if (!needsBackup) {
- return;
- }
-
- // Add and commit changes
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
- LOGGER.info("Committed backup: {}", commit.getId());
-
- // Reset the backup flag
- this.needsBackup = false;
- }
-
- @SuppressWarnings("checkstyle:TodoComment")
- public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
- try {
- Git git = Git.open(backupDir.toFile());
-
- git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
- // Add commits to staging Area
- git.add().addFilepattern(".").call();
-
- // Commit with a message
- git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
-
- LOGGER.info("Restored backup from Git repository and committed the changes");
- } catch (
- IOException |
- GitAPIException e) {
- LOGGER.error("Error while restoring the backup", e);
- }
- }
-
- public static void restoreBackupj(Path originalPath, Path backupDir, ObjectId objectId) {
- try {
-
- Git git = Git.open(backupDir.toFile());
- git.checkout().setName(objectId.getName()).call();
- /*
- faut une methode pour evite le branch nouveau
- need a method to avoid the new branch
- */
- LOGGER.info("Restored backup from Git repository");
- } catch (
- IOException |
- GitAPIException e) {
- LOGGER.error("Error while restoring the backup", e);
- }
- }
- /*
- compare what is in originalPath and last commit
- */
-
- public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
-
- File repoDir = backupDir.toFile();
- Repository repository = new FileRepositoryBuilder()
- .setGitDir(new File(repoDir, ".git"))
- .build();
- try (Git git = new Git(repository)) {
- ObjectId headCommitId = repository.resolve("HEAD"); // to get the latest commit id
- if (headCommitId == null) {
- // No commits in the repository, so there's no previous backup
- return false;
- }
- git.add().addFilepattern(originalPath.getFileName().toString()).call();
- String relativePath = backupDir.relativize(originalPath).toString();
- List diffs = git.diff()
- .setPathFilter(PathFilter.create(relativePath)) // Utiliser PathFilter ici
- .call();
- return !diffs.isEmpty();
- }
- }
-
- public List showDiffers(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
-
- File repoDir = backupDir.toFile();
- Repository repository = new FileRepositoryBuilder()
- .setGitDir(new File(repoDir, ".git"))
- .build();
- /*
- need a class to show the last ten backups indicating: date/ size/ number of entries
- */
-
- ObjectId oldCommit = repository.resolve(CommitId);
- ObjectId newCommit = repository.resolve("HEAD");
-
- FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
- DiffFormatter diffFr = new DiffFormatter(fos);
- diffFr.setRepository(repository);
- return diffFr.scan(oldCommit, newCommit);
- }
-
- // n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
-// and decrements by 1 when the user asks to see the pack of the 10 earlier versions
-// the scroll down: n->n+1 ; the scroll up: n->n-1
- public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
- List retrievedCommits = new ArrayList<>();
- // Open Git depository
- try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- // Use RevWalk to go through all commits
- try (RevWalk revWalk = new RevWalk(repository)) {
- // Start from HEAD
- RevCommit startCommit = revWalk.parseCommit(repository.resolve("HEAD"));
- revWalk.markStart(startCommit);
-
- int count = 0;
- int startIndex = n * 10;
- int endIndex = startIndex + 9;
-
- for (RevCommit commit : revWalk) {
- // Ignore commits before starting index
- if (count < startIndex) {
- count++;
- continue;
- }
- if (count >= endIndex) {
- break;
- }
- // Add commits to the main list
- retrievedCommits.add(commit);
- count++;
- }
- }
- }
-
- return retrievedCommits;
- }
-
- public List> retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
- List> commitDetails;
- try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- commitDetails = new ArrayList<>();
-
- // Browse the list of commits given as a parameter
- for (RevCommit commit : commits) {
- // A list to stock details about the commit
- List commitInfo = new ArrayList<>();
- commitInfo.add(commit.getName()); // ID of commit
-
- // Get the size of files changes by the commit
- try (TreeWalk treeWalk = new TreeWalk(repository)) {
- treeWalk.addTree(commit.getTree());
- treeWalk.setRecursive(true);
- long totalSize = 0;
-
- while (treeWalk.next()) {
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- totalSize += loader.getSize(); // size in bytes
- }
-
- // Convert the size to Kb or Mb
- String sizeFormatted = (totalSize > 1024 * 1024)
- ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
- : String.format("%.2f Ko", totalSize / 1024.0);
-
- commitInfo.add(sizeFormatted); // Add Formatted size
- }
-
- // adding date detail
- Date date= commit.getAuthorIdent().getWhen();
- commitInfo.add(date.toString());
- // Add list of details to the main list
- commitDetails.add(commitInfo);
- }
- }
-
- return commitDetails;
- }
- private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPath) {
- changeFilter.unregisterListener(this);
- changeFilter.shutdown();
- executor.shutdown();
-
- if (createBackup) {
- try {
- performBackup(backupDir,originalPath);
- } catch (IOException | GitAPIException e) {
- LOGGER.error("Error during shutdown backup", e);
- }
- }
- }
-}
-
-
-
-
-
-
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
index a45d7cbb9c1..653e6f505ca 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
@@ -181,7 +181,7 @@ void shouldCreateABackup(@TempDir Path customDir) throws Exception {
fullBackupPath.ifPresent(manager::performBackup);
manager.listen(new GroupUpdatedEvent(new MetaData()));
- BackupManager.shutdown(database, backupDir, true);
+ BackupManager.shutdown(database, backupDir, true, bibPath);
List files = Files.list(backupDir).sorted().toList();
// we only know the first backup path because the second one is created on shutdown
From d6df47a50160d9d9304424a963a280eafc955a22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Tue, 26 Nov 2024 22:39:38 +0100
Subject: [PATCH 22/84] Corrected errors related to BackupManagerGit class
---
.../gui/autosaveandbackup/BackupManager.java | 504 +++++++++++-------
.../autosaveandbackup/BackupManagerGit.java | 370 +++++++++++++
.../autosaveandbackup/BackupManagerOld.java | 398 --------------
.../jabref/gui/dialogs/BackupUIManager.java | 5 +-
.../gui/exporter/SaveDatabaseAction.java | 6 +-
.../logic/exporter/SaveConfiguration.java | 4 +-
.../org/jabref/logic/util/BackupFileType.java | 2 +-
.../jabref/logic/util/io/BackupFileUtil.java | 4 +-
.../autosaveandbackup/BackupManagerTest.java | 2 +-
9 files changed, 691 insertions(+), 604 deletions(-)
create mode 100644 src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
delete mode 100644 src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerOld.java
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
index 32b0729e9d1..8e468418908 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
@@ -1,46 +1,66 @@
package org.jabref.gui.autosaveandbackup;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Date;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.FileTime;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
+import java.util.Queue;
import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import javafx.scene.control.TableColumn;
+
import org.jabref.gui.LibraryTab;
+import org.jabref.gui.maintable.BibEntryTableViewModel;
+import org.jabref.gui.maintable.columns.MainTableColumn;
+import org.jabref.logic.bibtex.InvalidFieldValueException;
+import org.jabref.logic.exporter.AtomicFileWriter;
+import org.jabref.logic.exporter.BibWriter;
+import org.jabref.logic.exporter.BibtexDatabaseWriter;
+import org.jabref.logic.exporter.SelfContainedSaveConfiguration;
import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.logic.util.BackupFileType;
import org.jabref.logic.util.CoarseChangeFilter;
+import org.jabref.logic.util.io.BackupFileUtil;
+import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.database.event.BibDatabaseContextChangedEvent;
+import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.metadata.SaveOrder;
+import org.jabref.model.metadata.SelfContainedSaveOrder;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.diff.DiffEntry;
-import org.eclipse.jgit.diff.DiffFormatter;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.treewalk.filter.PathFilter;
+import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * Backups the given bib database file from {@link BibDatabaseContext} on every {@link BibDatabaseContextChangedEvent}.
+ * An intelligent {@link ExecutorService} with a {@link BlockingQueue} prevents a high load while making backups and
+ * rejects all redundant backup tasks. This class does not manage the .bak file which is created when opening a
+ * database.
+ */
+
public class BackupManager {
private static final Logger LOGGER = LoggerFactory.getLogger(BackupManager.class);
+ private static final int MAXIMUM_BACKUP_FILE_COUNT = 10;
+
private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
- private static Set runningInstances = new HashSet();
+ private static Set runningInstances = new HashSet<>();
private final BibDatabaseContext bibDatabaseContext;
private final CliPreferences preferences;
@@ -48,11 +68,13 @@ public class BackupManager {
private final CoarseChangeFilter changeFilter;
private final BibEntryTypesManager entryTypesManager;
private final LibraryTab libraryTab;
- private final Git git;
+ // Contains a list of all backup paths
+ // During writing, the less recent backup file is deleted
+ private final Queue backupFilesQueue = new LinkedBlockingQueue<>();
private boolean needsBackup = false;
- BackupManager(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ BackupManager(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
this.bibDatabaseContext = bibDatabaseContext;
this.entryTypesManager = entryTypesManager;
this.preferences = preferences;
@@ -61,224 +83,316 @@ public class BackupManager {
changeFilter = new CoarseChangeFilter(bibDatabaseContext);
changeFilter.registerListener(this);
+ }
- // Initialize Git repository
- FileRepositoryBuilder builder = new FileRepositoryBuilder();
- git = new Git(builder.setGitDir(new File(preferences.getFilePreferences().getBackupDirectory().toFile(), ".git"))
- .readEnvironment()
- .findGitDir()
- .build());
- if (git.getRepository().getObjectDatabase().exists()) {
- LOGGER.info("Git repository already exists");
- } else {
- git.init().call();
- LOGGER.info("Initialized new Git repository");
- }
+ /**
+ * Determines the most recent backup file name
+ */
+ static Path getBackupPathForNewBackup(Path originalPath, Path backupDir) {
+ return BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(originalPath, BackupFileType.BACKUP, backupDir);
}
- public BackupManager start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
+ /**
+ * Determines the most recent existing backup file name
+ */
+ static Optional getLatestBackupPath(Path originalPath, Path backupDir) {
+ return BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
+ }
+
+ /**
+ * Starts the BackupManager which is associated with the given {@link BibDatabaseContext}. As long as no database
+ * file is present in {@link BibDatabaseContext}, the {@link BackupManager} will do nothing.
+ *
+ * This method is not thread-safe. The caller has to ensure that this method is not called in parallel.
+ *
+ * @param bibDatabaseContext Associated {@link BibDatabaseContext}
+ */
+ public static BackupManager start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
BackupManager BackupManager = new BackupManager(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- BackupManager.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
+ BackupManager.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
runningInstances.add(BackupManager);
return BackupManager;
}
- @SuppressWarnings({"checkstyle:NoWhitespaceBefore", "checkstyle:WhitespaceAfter"})
- public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup, Path originalPath) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup, originalPath));
+ /**
+ * Marks the backup as discarded at the library which is associated with the given {@link BibDatabaseContext}.
+ *
+ * @param bibDatabaseContext Associated {@link BibDatabaseContext}
+ */
+ public static void discardBackup(BibDatabaseContext bibDatabaseContext, Path backupDir) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(BackupManager -> BackupManager.discardBackup(backupDir));
+ }
+
+ /**
+ * Shuts down the BackupManager which is associated with the given {@link BibDatabaseContext}.
+ *
+ * @param bibDatabaseContext Associated {@link BibDatabaseContext}
+ * @param createBackup True, if a backup should be created
+ * @param backupDir The path to the backup directory
+ */
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(BackupManager -> BackupManager.shutdown(backupDir, createBackup));
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
}
- @SuppressWarnings({"checkstyle:WhitespaceAfter", "checkstyle:LeftCurly"})
- private void startBackupTask(Path backupDir, Path originalPath) {
- executor.scheduleAtFixedRate(
- () -> {
- try {
- performBackup(backupDir, originalPath);
- } catch (
- IOException |
- GitAPIException e) {
- LOGGER.error("Error during backup", e);
- }
- },
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- TimeUnit.SECONDS);
+ /**
+ * Checks whether a backup file exists for the given database file. If it exists, it is checked whether it is
+ * newer and different from the original.
+ *
+ * In case a discarded file is present, the method also returns false
, See also {@link #discardBackup(Path)}.
+ *
+ * @param originalPath Path to the file a backup should be checked for. Example: jabref.bib.
+ *
+ * @return true
if backup file exists AND differs from originalPath. false
is the
+ * "default" return value in the good case. In case a discarded file exists, false
is returned, too.
+ * In the case of an exception true
is returned to ensure that the user checks the output.
+ */
+ public static boolean backupFileDiffers(Path originalPath, Path backupDir) {
+ Path discardedFile = determineDiscardedFile(originalPath, backupDir);
+ if (Files.exists(discardedFile)) {
+ try {
+ Files.delete(discardedFile);
+ } catch (IOException e) {
+ LOGGER.error("Could not remove discarded file {}", discardedFile, e);
+ return true;
+ }
+ return false;
+ }
+ return getLatestBackupPath(originalPath, backupDir).map(latestBackupPath -> {
+ FileTime latestBackupFileLastModifiedTime;
+ try {
+ latestBackupFileLastModifiedTime = Files.getLastModifiedTime(latestBackupPath);
+ } catch (IOException e) {
+ LOGGER.debug("Could not get timestamp of backup file {}", latestBackupPath, e);
+ // If we cannot get the timestamp, we do show any warning
+ return false;
+ }
+ FileTime currentFileLastModifiedTime;
+ try {
+ currentFileLastModifiedTime = Files.getLastModifiedTime(originalPath);
+ } catch (IOException e) {
+ LOGGER.debug("Could not get timestamp of current file file {}", originalPath, e);
+ // If we cannot get the timestamp, we do show any warning
+ return false;
+ }
+ if (latestBackupFileLastModifiedTime.compareTo(currentFileLastModifiedTime) <= 0) {
+ // Backup is older than current file
+ // We treat the backup as non-different (even if it could differ)
+ return false;
+ }
+ try {
+ boolean result = Files.mismatch(originalPath, latestBackupPath) != -1L;
+ if (result) {
+ LOGGER.info("Backup file {} differs from current file {}", latestBackupPath, originalPath);
+ }
+ return result;
+ } catch (IOException e) {
+ LOGGER.debug("Could not compare original file and backup file.", e);
+ // User has to investigate in this case
+ return true;
+ }
+ }).orElse(false);
}
- private void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
- /*
- needsBackup must be initialized
- */
- needsBackup = BackupManager.backupGitDiffers(backupDir, originalPath);
- if (!needsBackup) {
+ /**
+ * Restores the backup file by copying and overwriting the original one.
+ *
+ * @param originalPath Path to the file which should be equalized to the backup file.
+ */
+ public static void restoreBackup(Path originalPath, Path backupDir) {
+ Optional backupPath = getLatestBackupPath(originalPath, backupDir);
+ if (backupPath.isEmpty()) {
+ LOGGER.error("There is no backup file");
return;
}
-
- // Add and commit changes
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
- LOGGER.info("Committed backup: {}", commit.getId());
-
- // Reset the backup flag
- this.needsBackup = false;
+ try {
+ Files.copy(backupPath.get(), originalPath, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ LOGGER.error("Error while restoring the backup file.", e);
+ }
}
- @SuppressWarnings("checkstyle:TodoComment")
- public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
- try {
- Git git = Git.open(backupDir.toFile());
+ Optional determineBackupPathForNewBackup(Path backupDir) {
+ return bibDatabaseContext.getDatabasePath().map(path -> BackupManager.getBackupPathForNewBackup(path, backupDir));
+ }
- git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
- // Add commits to staging Area
- git.add().addFilepattern(".").call();
+ /**
+ * This method is called as soon as the scheduler says: "Do the backup"
+ *
+ * SIDE EFFECT: Deletes oldest backup file
+ *
+ * @param backupPath the full path to the file where the library should be backed up to
+ */
+ void performBackup(Path backupPath) {
+ if (!needsBackup) {
+ return;
+ }
- // Commit with a message
- git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
+ // We opted for "while" to delete backups in case there are more than 10
+ while (backupFilesQueue.size() >= MAXIMUM_BACKUP_FILE_COUNT) {
+ Path oldestBackupFile = backupFilesQueue.poll();
+ try {
+ Files.delete(oldestBackupFile);
+ } catch (IOException e) {
+ LOGGER.error("Could not delete backup file {}", oldestBackupFile, e);
+ }
+ }
- LOGGER.info("Restored backup from Git repository and committed the changes");
- } catch (
- IOException |
- GitAPIException e) {
- LOGGER.error("Error while restoring the backup", e);
+ // l'ordre dans lequel les entrées BibTeX doivent être écrites dans le fichier de sauvegarde.
+ // Si l'utilisateur a trié la table d'affichage des entrées dans JabRef, cet ordre est récupéré.
+ // Sinon, un ordre par défaut est utilisé.
+ // code similar to org.jabref.gui.exporter.SaveDatabaseAction.saveDatabase
+ SelfContainedSaveOrder saveOrder = bibDatabaseContext
+ .getMetaData().getSaveOrder()
+ .map(so -> {
+ if (so.getOrderType() == SaveOrder.OrderType.TABLE) {
+ // We need to "flatten out" SaveOrder.OrderType.TABLE as BibWriter does not have access to preferences
+ List> sortOrder = libraryTab.getMainTable().getSortOrder();
+ return new SelfContainedSaveOrder(
+ SaveOrder.OrderType.SPECIFIED,
+ sortOrder.stream()
+ .filter(col -> col instanceof MainTableColumn>)
+ .map(column -> ((MainTableColumn>) column).getModel())
+ .flatMap(model -> model.getSortCriteria().stream())
+ .toList());
+ } else {
+ return SelfContainedSaveOrder.of(so);
+ }
+ })
+ .orElse(SaveOrder.getDefaultSaveOrder());
+
+ // Elle configure la sauvegarde, en indiquant qu'aucune sauvegarde supplémentaire (backup) ne doit être créée,
+ // que l'ordre de sauvegarde doit être celui défini, et que les entrées doivent être formatées selon les préférences
+ // utilisateur.
+ SelfContainedSaveConfiguration saveConfiguration = (SelfContainedSaveConfiguration) new SelfContainedSaveConfiguration()
+ .withMakeBackup(false)
+ .withSaveOrder(saveOrder)
+ .withReformatOnSave(preferences.getLibraryPreferences().shouldAlwaysReformatOnSave());
+
+ // "Clone" the database context
+ // We "know" that "only" the BibEntries might be changed during writing (see [org.jabref.logic.exporter.BibDatabaseWriter.savePartOfDatabase])
+ // Chaque entrée BibTeX (comme un article, livre, etc.) est clonée en utilisant la méthode clone().
+ // Cela garantit que les modifications faites pendant la sauvegarde n'affecteront pas l'entrée originale.
+ List list = bibDatabaseContext.getDatabase().getEntries().stream()
+ .map(BibEntry::clone)
+ .map(BibEntry.class::cast)
+ .toList();
+ BibDatabase bibDatabaseClone = new BibDatabase(list);
+ BibDatabaseContext bibDatabaseContextClone = new BibDatabaseContext(bibDatabaseClone, bibDatabaseContext.getMetaData());
+ // Elle définit l'encodage à utiliser pour écrire le fichier. Cela garantit que les caractères spéciaux sont bien sauvegardés.
+ Charset encoding = bibDatabaseContext.getMetaData().getEncoding().orElse(StandardCharsets.UTF_8);
+ // We want to have successful backups only
+ // Thus, we do not use a plain "FileWriter", but the "AtomicFileWriter"
+ // Example: What happens if one hard powers off the machine (or kills the jabref process) during writing of the backup?
+ // This MUST NOT create a broken backup file that then jabref wants to "restore" from?
+ try (Writer writer = new AtomicFileWriter(backupPath, encoding, false)) {
+ BibWriter bibWriter = new BibWriter(writer, bibDatabaseContext.getDatabase().getNewLineSeparator());
+ new BibtexDatabaseWriter(
+ bibWriter,
+ saveConfiguration,
+ preferences.getFieldPreferences(),
+ preferences.getCitationKeyPatternPreferences(),
+ entryTypesManager)
+ // we save the clone to prevent the original database (and thus the UI) from being changed
+ .saveDatabase(bibDatabaseContextClone);
+ backupFilesQueue.add(backupPath);
+
+ // We wrote the file successfully
+ // Thus, we currently do not need any new backup
+ this.needsBackup = false;
+ } catch (IOException e) {
+ logIfCritical(backupPath, e);
}
}
- public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
+ private static Path determineDiscardedFile(Path file, Path backupDir) {
+ return backupDir.resolve(BackupFileUtil.getUniqueFilePrefix(file) + "--" + file.getFileName() + "--discarded");
+ }
- File repoDir = backupDir.toFile();
- Repository repository = new FileRepositoryBuilder()
- .setGitDir(new File(repoDir, ".git"))
- .build();
- try (Git git = new Git(repository)) {
- ObjectId headCommitId = repository.resolve("HEAD"); // to get the latest commit id
- if (headCommitId == null) {
- // No commits in the repository, so there's no previous backup
- return false;
- }
- git.add().addFilepattern(originalPath.getFileName().toString()).call();
- String relativePath = backupDir.relativize(originalPath).toString();
- List diffs = git.diff()
- .setPathFilter(PathFilter.create(relativePath)) // Utiliser PathFilter ici
- .call();
- return !diffs.isEmpty();
+ /**
+ * Marks the backups as discarded.
+ *
+ * We do not delete any files, because the user might want to recover old backup files.
+ * Therefore, we mark discarded backups by a --discarded file.
+ */
+ public void discardBackup(Path backupDir) {
+ Path path = determineDiscardedFile(bibDatabaseContext.getDatabasePath().get(), backupDir);
+ try {
+ Files.createFile(path);
+ } catch (IOException e) {
+ LOGGER.info("Could not create backup file {}", path, e);
}
}
- public List showDiffers(Path originalPath, Path backupDir, String CommitId) throws IOException, GitAPIException {
-
- File repoDir = backupDir.toFile();
- Repository repository = new FileRepositoryBuilder()
- .setGitDir(new File(repoDir, ".git"))
- .build();
- /*
- need a class to show the last ten backups indicating: date/ size/ number of entries
- */
-
- ObjectId oldCommit = repository.resolve(CommitId);
- ObjectId newCommit = repository.resolve("HEAD");
+ private void logIfCritical(Path backupPath, IOException e) {
+ Throwable innermostCause = e;
+ while (innermostCause.getCause() != null) {
+ innermostCause = innermostCause.getCause();
+ }
+ boolean isErrorInField = innermostCause instanceof InvalidFieldValueException;
- FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
- DiffFormatter diffFr = new DiffFormatter(fos);
- diffFr.setRepository(repository);
- return diffFr.scan(oldCommit, newCommit);
+ // do not print errors in field values into the log during autosave
+ if (!isErrorInField) {
+ LOGGER.error("Error while saving to file {}", backupPath, e);
+ }
}
- // n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
-// and decrements by 1 when the user asks to see the pack of the 10 earlier versions
-// the scroll down: n->n+1 ; the scroll up: n->n-1
- public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
- List retrievedCommits = new ArrayList<>();
- // Open Git depository
- try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- // Use RevWalk to go through all commits
- try (RevWalk revWalk = new RevWalk(repository)) {
- // Start from HEAD
- RevCommit startCommit = revWalk.parseCommit(repository.resolve("HEAD"));
- revWalk.markStart(startCommit);
-
- int count = 0;
- int startIndex = n * 10;
- int endIndex = startIndex + 9;
-
- for (RevCommit commit : revWalk) {
- // Ignore commits before starting index
- if (count < startIndex) {
- count++;
- continue;
- }
- if (count >= endIndex) {
- break;
- }
- // Add commits to the main list
- retrievedCommits.add(commit);
- count++;
- }
- }
+ @Subscribe
+ public synchronized void listen(@SuppressWarnings("unused") BibDatabaseContextChangedEvent event) {
+ if (!event.isFilteredOut()) {
+ this.needsBackup = true;
}
-
- return retrievedCommits;
}
- @SuppressWarnings("checkstyle:WhitespaceAround")
- public List> retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
- List> commitDetails;
- try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- commitDetails = new ArrayList<>();
-
- // Browse the list of commits given as a parameter
- for (RevCommit commit : commits) {
- // A list to stock details about the commit
- List commitInfo = new ArrayList<>();
- commitInfo.add(commit.getName()); // ID of commit
-
- // Get the size of files changes by the commit
- try (TreeWalk treeWalk = new TreeWalk(repository)) {
- treeWalk.addTree(commit.getTree());
- treeWalk.setRecursive(true);
- long totalSize = 0;
-
- while (treeWalk.next()) {
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- totalSize += loader.getSize(); // size in bytes
- }
-
- // Convert the size to Kb or Mb
- String sizeFormatted = (totalSize > 1024 * 1024)
- ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
- : String.format("%.2f Ko", totalSize / 1024.0);
+ private void startBackupTask(Path backupDir) {
+ fillQueue(backupDir);
+ // remplie backupFilesQueue les files .sav de le meme bibl
- commitInfo.add(sizeFormatted); // Add Formatted size
- }
+ executor.scheduleAtFixedRate(
+ // We need to determine the backup path on each action, because we use the timestamp in the filename
+ () -> determineBackupPathForNewBackup(backupDir).ifPresent(path -> this.performBackup(path)),
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ TimeUnit.SECONDS);
+ }
+ // La méthode fillQueue(backupDir) est définie dans le code et son rôle est de lister et d'ajouter
+// les fichiers de sauvegarde existants dans une file d'attente,
- // adding date detail
- Date date = commit.getAuthorIdent().getWhen();
- commitInfo.add(date.toString());
- // Add list of details to the main list
- commitDetails.add(commitInfo);
- }
+ private void fillQueue(Path backupDir) {
+ if (!Files.exists(backupDir)) {
+ return;
}
-
- return commitDetails;
+ bibDatabaseContext.getDatabasePath().ifPresent(databasePath -> {
+ // code similar to {@link org.jabref.logic.util.io.BackupFileUtil.getPathOfLatestExisingBackupFile}
+ final String prefix = BackupFileUtil.getUniqueFilePrefix(databasePath) + "--" + databasePath.getFileName();
+ try {
+ List allSavFiles = Files.list(backupDir)
+ // just list the .sav belonging to the given targetFile
+ .filter(p -> p.getFileName().toString().startsWith(prefix))
+ // tous les files .sav commencerait par ce prefix
+ .sorted().toList();
+ backupFilesQueue.addAll(allSavFiles);
+ } catch (IOException e) {
+ LOGGER.error("Could not determine most recent file", e);
+ }
+ });
}
- private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPath) {
+ /**
+ * Unregisters the BackupManager from the eventBus of {@link BibDatabaseContext}.
+ * This method should only be used when closing a database/JabRef in a normal way.
+ *
+ * @param backupDir The backup directory
+ * @param createBackup If the backup manager should still perform a backup
+ */
+ private void shutdown(Path backupDir, boolean createBackup) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
executor.shutdown();
if (createBackup) {
- try {
- performBackup(backupDir, originalPath);
- } catch (IOException | GitAPIException e) {
- LOGGER.error("Error during shutdown backup", e);
- }
+ // Ensure that backup is a recent one
+ determineBackupPathForNewBackup(backupDir).ifPresent(this::performBackup);
}
}
}
-
-
-
-
-
-
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
new file mode 100644
index 00000000000..dae2ed05250
--- /dev/null
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -0,0 +1,370 @@
+package org.jabref.gui.autosaveandbackup;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.jabref.gui.LibraryTab;
+import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.logic.util.CoarseChangeFilter;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntryTypesManager;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.diff.DiffEntry;
+import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BackupManagerGit {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackupManagerGit.class);
+
+ private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
+
+ private static Set runningInstances = new HashSet();
+
+ private final BibDatabaseContext bibDatabaseContext;
+ private final CliPreferences preferences;
+ private final ScheduledThreadPoolExecutor executor;
+ private final CoarseChangeFilter changeFilter;
+ private final BibEntryTypesManager entryTypesManager;
+ private final LibraryTab libraryTab;
+ private final Git git;
+
+ private boolean needsBackup = false;
+
+ BackupManagerGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ this.bibDatabaseContext = bibDatabaseContext;
+ this.entryTypesManager = entryTypesManager;
+ this.preferences = preferences;
+ this.executor = new ScheduledThreadPoolExecutor(2);
+ this.libraryTab = libraryTab;
+
+ changeFilter = new CoarseChangeFilter(bibDatabaseContext);
+ changeFilter.registerListener(this);
+
+ // Initialize Git repository
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
+ git = new Git(builder.setGitDir(new File(preferences.getFilePreferences().getBackupDirectory().toFile(), ".git"))
+ .readEnvironment()
+ .findGitDir()
+ .build());
+ if (git.getRepository().getObjectDatabase().exists()) {
+ LOGGER.info("Git repository already exists");
+ } else {
+ git.init().call();
+ LOGGER.info("Initialized new Git repository");
+ }
+ }
+
+ /**
+ * Starts a new BackupManagerGit instance and begins the backup task.
+ *
+ * @param libraryTab the library tab
+ * @param bibDatabaseContext the BibDatabaseContext to be backed up
+ * @param entryTypesManager the BibEntryTypesManager
+ * @param preferences the CLI preferences
+ * @param originalPath the original path of the file to be backed up
+ * @return the started BackupManagerGit instance
+ * @throws IOException if an I/O error occurs
+ * @throws GitAPIException if a Git API error occurs
+ */
+
+ public BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
+ BackupManagerGit BackupManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ BackupManager.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
+ runningInstances.add(BackupManager);
+ return BackupManager;
+ }
+
+ /**
+ * Shuts down the BackupManagerGit instances associated with the given BibDatabaseContext.
+ *
+ * @param bibDatabaseContext the BibDatabaseContext
+ * @param backupDir the backup directory
+ * @param createBackup whether to create a backup before shutting down
+ * @param originalPath the original path of the file to be backed up
+ */
+
+ @SuppressWarnings({"checkstyle:NoWhitespaceBefore", "checkstyle:WhitespaceAfter"})
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup, Path originalPath) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup, originalPath));
+ runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
+ }
+
+ /**
+ * Starts the backup task that periodically checks for changes and commits them to the Git repository.
+ *
+ * @param backupDir the backup directory
+ * @param originalPath the original path of the file to be backed up
+ */
+
+ @SuppressWarnings({"checkstyle:WhitespaceAfter", "checkstyle:LeftCurly"})
+ private void startBackupTask(Path backupDir, Path originalPath) {
+ executor.scheduleAtFixedRate(
+ () -> {
+ try {
+ performBackup(backupDir, originalPath);
+ } catch (
+ IOException |
+ GitAPIException e) {
+ LOGGER.error("Error during backup", e);
+ }
+ },
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ TimeUnit.SECONDS);
+ }
+
+ /**
+ * Performs the backup by checking for changes and committing them to the Git repository.
+ *
+ * @param backupDir the backup directory
+ * @param originalPath the original path of the file to be backed up
+ * @throws IOException if an I/O error occurs
+ * @throws GitAPIException if a Git API error occurs
+ */
+
+ private void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
+ /*
+ needsBackup must be initialized
+ */
+ needsBackup = BackupManagerGit.backupGitDiffers(backupDir, originalPath);
+ if (!needsBackup) {
+ return;
+ }
+
+ // Add and commit changes
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
+ LOGGER.info("Committed backup: {}", commit.getId());
+
+ // Reset the backup flag
+ this.needsBackup = false;
+ }
+
+ /**
+ * Restores the backup from the specified commit.
+ *
+ * @param originalPath the original path of the file to be restored
+ * @param backupDir the backup directory
+ * @param objectId the commit ID to restore from
+ */
+
+ @SuppressWarnings("checkstyle:TodoComment")
+ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
+ try {
+ Git git = Git.open(backupDir.toFile());
+
+ git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
+ // Add commits to staging Area
+ git.add().addFilepattern(".").call();
+
+ // Commit with a message
+ git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
+
+ LOGGER.info("Restored backup from Git repository and committed the changes");
+ } catch (
+ IOException |
+ GitAPIException e) {
+ LOGGER.error("Error while restoring the backup", e);
+ }
+ }
+
+ /**
+ * Checks if there are differences between the original file and the backup.
+ *
+ * @param originalPath the original path of the file
+ * @param backupDir the backup directory
+ * @return true if there are differences, false otherwise
+ * @throws IOException if an I/O error occurs
+ * @throws GitAPIException if a Git API error occurs
+ */
+
+ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
+
+ File repoDir = backupDir.toFile();
+ Repository repository = new FileRepositoryBuilder()
+ .setGitDir(new File(repoDir, ".git"))
+ .build();
+ try (Git git = new Git(repository)) {
+ ObjectId headCommitId = repository.resolve("HEAD"); // to get the latest commit id
+ if (headCommitId == null) {
+ // No commits in the repository, so there's no previous backup
+ return false;
+ }
+ git.add().addFilepattern(originalPath.getFileName().toString()).call();
+ String relativePath = backupDir.relativize(originalPath).toString();
+ List diffs = git.diff()
+ .setPathFilter(PathFilter.create(relativePath)) // Utiliser PathFilter ici
+ .call();
+ return !diffs.isEmpty();
+ }
+ }
+
+ /**
+ * Shows the differences between the specified commit and the latest commit.
+ *
+ * @param originalPath the original path of the file
+ * @param backupDir the backup directory
+ * @param commitId the commit ID to compare with the latest commit
+ * @return a list of DiffEntry objects representing the differences
+ * @throws IOException if an I/O error occurs
+ * @throws GitAPIException if a Git API error occurs
+ */
+
+ public List showDiffers(Path originalPath, Path backupDir, String commitId) throws IOException, GitAPIException {
+
+ File repoDir = backupDir.toFile();
+ Repository repository = new FileRepositoryBuilder()
+ .setGitDir(new File(repoDir, ".git"))
+ .build();
+ /*
+ need a class to show the last ten backups indicating: date/ size/ number of entries
+ */
+
+ ObjectId oldCommit = repository.resolve(commitId);
+ ObjectId newCommit = repository.resolve("HEAD");
+
+ FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
+ DiffFormatter diffFr = new DiffFormatter(fos);
+ diffFr.setRepository(repository);
+ return diffFr.scan(oldCommit, newCommit);
+ }
+
+ // n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
+// and decrements by 1 when the user asks to see the pack of the 10 earlier versions
+// the scroll down: n->n+1 ; the scroll up: n->n-1
+ public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
+ List retrievedCommits = new ArrayList<>();
+ // Open Git depository
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
+ // Use RevWalk to go through all commits
+ try (RevWalk revWalk = new RevWalk(repository)) {
+ // Start from HEAD
+ RevCommit startCommit = revWalk.parseCommit(repository.resolve("HEAD"));
+ revWalk.markStart(startCommit);
+
+ int count = 0;
+ int startIndex = n * 10;
+ int endIndex = startIndex + 9;
+
+ for (RevCommit commit : revWalk) {
+ // Ignore commits before starting index
+ if (count < startIndex) {
+ count++;
+ continue;
+ }
+ if (count >= endIndex) {
+ break;
+ }
+ // Add commits to the main list
+ retrievedCommits.add(commit);
+ count++;
+ }
+ }
+ }
+
+ return retrievedCommits;
+ }
+
+
+ /**
+ * Retrieves detailed information about the specified commits.
+ *
+ * @param commits the list of commits to retrieve details for
+ * @param backupDir the backup directory
+ * @return a list of lists, each containing details about a commit
+ * @throws IOException if an I/O error occurs
+ * @throws GitAPIException if a Git API error occurs
+ */
+
+ @SuppressWarnings("checkstyle:WhitespaceAround")
+ public List> retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
+ List> commitDetails;
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
+ commitDetails = new ArrayList<>();
+
+ // Browse the list of commits given as a parameter
+ for (RevCommit commit : commits) {
+ // A list to stock details about the commit
+ List commitInfo = new ArrayList<>();
+ commitInfo.add(commit.getName()); // ID of commit
+
+ // Get the size of files changes by the commit
+ try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.addTree(commit.getTree());
+ treeWalk.setRecursive(true);
+ long totalSize = 0;
+
+ while (treeWalk.next()) {
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ totalSize += loader.getSize(); // size in bytes
+ }
+
+ // Convert the size to Kb or Mb
+ String sizeFormatted = (totalSize > 1024 * 1024)
+ ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
+ : String.format("%.2f Ko", totalSize / 1024.0);
+
+ commitInfo.add(sizeFormatted); // Add Formatted size
+ }
+
+ // adding date detail
+ Date date = commit.getAuthorIdent().getWhen();
+ commitInfo.add(date.toString());
+ // Add list of details to the main list
+ commitDetails.add(commitInfo);
+ }
+ }
+
+ return commitDetails;
+ }
+
+ /**
+ * Shuts down the JGit components and optionally creates a backup.
+ *
+ * @param backupDir the backup directory
+ * @param createBackup whether to create a backup before shutting down
+ * @param originalPath the original path of the file to be backed up
+ */
+
+ private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPath) {
+ changeFilter.unregisterListener(this);
+ changeFilter.shutdown();
+ executor.shutdown();
+
+ if (createBackup) {
+ try {
+ performBackup(backupDir, originalPath);
+ } catch (IOException | GitAPIException e) {
+ LOGGER.error("Error during shutdown backup", e);
+ }
+ }
+ }
+}
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerOld.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerOld.java
deleted file mode 100644
index 32ef596f8e7..00000000000
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerOld.java
+++ /dev/null
@@ -1,398 +0,0 @@
-package org.jabref.gui.autosaveandbackup;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.FileTime;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-import javafx.scene.control.TableColumn;
-
-import org.jabref.gui.LibraryTab;
-import org.jabref.gui.maintable.BibEntryTableViewModel;
-import org.jabref.gui.maintable.columns.MainTableColumn;
-import org.jabref.logic.bibtex.InvalidFieldValueException;
-import org.jabref.logic.exporter.AtomicFileWriter;
-import org.jabref.logic.exporter.BibWriter;
-import org.jabref.logic.exporter.BibtexDatabaseWriter;
-import org.jabref.logic.exporter.SelfContainedSaveConfiguration;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.util.BackupFileType;
-import org.jabref.logic.util.CoarseChangeFilter;
-import org.jabref.logic.util.io.BackupFileUtil;
-import org.jabref.model.database.BibDatabase;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.database.event.BibDatabaseContextChangedEvent;
-import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.BibEntryTypesManager;
-import org.jabref.model.metadata.SaveOrder;
-import org.jabref.model.metadata.SelfContainedSaveOrder;
-
-import com.google.common.eventbus.Subscribe;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Backups the given bib database file from {@link BibDatabaseContext} on every {@link BibDatabaseContextChangedEvent}.
- * An intelligent {@link ExecutorService} with a {@link BlockingQueue} prevents a high load while making backups and
- * rejects all redundant backup tasks. This class does not manage the .bak file which is created when opening a
- * database.
- */
-@SuppressWarnings("checkstyle:WhitespaceAround")
-public class BackupManagerOld {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(BackupManagerOld.class);
-
- private static final int MAXIMUM_BACKUP_FILE_COUNT = 10;
-
- private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
-
- private static Set runningInstances = new HashSet<>();
-
- private final BibDatabaseContext bibDatabaseContext;
- private final CliPreferences preferences;
- private final ScheduledThreadPoolExecutor executor;
- private final CoarseChangeFilter changeFilter;
- private final BibEntryTypesManager entryTypesManager;
- private final LibraryTab libraryTab;
-
- // Contains a list of all backup paths
- // During writing, the less recent backup file is deleted
- private final Queue backupFilesQueue = new LinkedBlockingQueue<>();
- private boolean needsBackup = false;
-
- BackupManagerOld(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
- this.bibDatabaseContext = bibDatabaseContext;
- this.entryTypesManager = entryTypesManager;
- this.preferences = preferences;
- this.executor = new ScheduledThreadPoolExecutor(2);
- this.libraryTab = libraryTab;
-
- changeFilter = new CoarseChangeFilter(bibDatabaseContext);
- changeFilter.registerListener(this);
- }
-
- /**
- * Determines the most recent backup file name
- */
- static Path getBackupPathForNewBackup(Path originalPath, Path backupDir) {
- return BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(originalPath, BackupFileType.BACKUP, backupDir);
- }
-
- /**
- * Determines the most recent existing backup file name
- */
- static Optional getLatestBackupPath(Path originalPath, Path backupDir) {
- return BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
- }
-
- /**
- * Starts the BackupManagerOld which is associated with the given {@link BibDatabaseContext}. As long as no database
- * file is present in {@link BibDatabaseContext}, the {@link BackupManagerOld} will do nothing.
- *
- * This method is not thread-safe. The caller has to ensure that this method is not called in parallel.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- */
- public static BackupManagerOld start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
- BackupManagerOld BackupManagerOld = new BackupManagerOld(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- BackupManagerOld.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
- runningInstances.add(BackupManagerOld);
- return BackupManagerOld;
- }
-
- /**
- * Marks the backup as discarded at the library which is associated with the given {@link BibDatabaseContext}.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- */
- public static void discardBackup(BibDatabaseContext bibDatabaseContext, Path backupDir) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(BackupManagerOld -> BackupManagerOld.discardBackup(backupDir));
- }
-
- /**
- * Shuts down the BackupManagerOld which is associated with the given {@link BibDatabaseContext}.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- * @param createBackup True, if a backup should be created
- * @param backupDir The path to the backup directory
- */
- public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(BackupManagerOld -> BackupManagerOld.shutdown(backupDir, createBackup));
- runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
- }
-
- /**
- * Checks whether a backup file exists for the given database file. If it exists, it is checked whether it is
- * newer and different from the original.
- *
- * In case a discarded file is present, the method also returns false
, See also {@link #discardBackup(Path)}.
- *
- * @param originalPath Path to the file a backup should be checked for. Example: jabref.bib.
- *
- * @return true
if backup file exists AND differs from originalPath. false
is the
- * "default" return value in the good case. In case a discarded file exists, false
is returned, too.
- * In the case of an exception true
is returned to ensure that the user checks the output.
- */
- public static boolean backupFileDiffers(Path originalPath, Path backupDir) {
- Path discardedFile = determineDiscardedFile(originalPath, backupDir);
- if (Files.exists(discardedFile)) {
- try {
- Files.delete(discardedFile);
- } catch (IOException e) {
- LOGGER.error("Could not remove discarded file {}", discardedFile, e);
- return true;
- }
- return false;
- }
- return getLatestBackupPath(originalPath, backupDir).map(latestBackupPath -> {
- FileTime latestBackupFileLastModifiedTime;
- try {
- latestBackupFileLastModifiedTime = Files.getLastModifiedTime(latestBackupPath);
- } catch (IOException e) {
- LOGGER.debug("Could not get timestamp of backup file {}", latestBackupPath, e);
- // If we cannot get the timestamp, we do show any warning
- return false;
- }
- FileTime currentFileLastModifiedTime;
- try {
- currentFileLastModifiedTime = Files.getLastModifiedTime(originalPath);
- } catch (IOException e) {
- LOGGER.debug("Could not get timestamp of current file file {}", originalPath, e);
- // If we cannot get the timestamp, we do show any warning
- return false;
- }
- if (latestBackupFileLastModifiedTime.compareTo(currentFileLastModifiedTime) <= 0) {
- // Backup is older than current file
- // We treat the backup as non-different (even if it could differ)
- return false;
- }
- try {
- boolean result = Files.mismatch(originalPath, latestBackupPath) != -1L;
- if (result) {
- LOGGER.info("Backup file {} differs from current file {}", latestBackupPath, originalPath);
- }
- return result;
- } catch (IOException e) {
- LOGGER.debug("Could not compare original file and backup file.", e);
- // User has to investigate in this case
- return true;
- }
- }).orElse(false);
- }
-
- /**
- * Restores the backup file by copying and overwriting the original one.
- *
- * @param originalPath Path to the file which should be equalized to the backup file.
- */
- public static void restoreBackup(Path originalPath, Path backupDir) {
- Optional backupPath = getLatestBackupPath(originalPath, backupDir);
- if (backupPath.isEmpty()) {
- LOGGER.error("There is no backup file");
- return;
- }
- try {
- Files.copy(backupPath.get(), originalPath, StandardCopyOption.REPLACE_EXISTING);
- } catch (IOException e) {
- LOGGER.error("Error while restoring the backup file.", e);
- }
- }
-
- Optional determineBackupPathForNewBackup(Path backupDir) {
- return bibDatabaseContext.getDatabasePath().map(path -> BackupManagerOld.getBackupPathForNewBackup(path, backupDir));
- }
-
- /**
- * This method is called as soon as the scheduler says: "Do the backup"
- *
- * SIDE EFFECT: Deletes oldest backup file
- *
- * @param backupPath the full path to the file where the library should be backed up to
- */
- void performBackup(Path backupPath) {
- if (!needsBackup) {
- return;
- }
-
- // We opted for "while" to delete backups in case there are more than 10
- while (backupFilesQueue.size() >= MAXIMUM_BACKUP_FILE_COUNT) {
- Path oldestBackupFile = backupFilesQueue.poll();
- try {
- Files.delete(oldestBackupFile);
- } catch (IOException e) {
- LOGGER.error("Could not delete backup file {}", oldestBackupFile, e);
- }
- }
-
- // l'ordre dans lequel les entrées BibTeX doivent être écrites dans le fichier de sauvegarde.
- // Si l'utilisateur a trié la table d'affichage des entrées dans JabRef, cet ordre est récupéré.
- // Sinon, un ordre par défaut est utilisé.
- // code similar to org.jabref.gui.exporter.SaveDatabaseAction.saveDatabase
- SelfContainedSaveOrder saveOrder = bibDatabaseContext
- .getMetaData().getSaveOrder()
- .map(so -> {
- if (so.getOrderType() == SaveOrder.OrderType.TABLE) {
- // We need to "flatten out" SaveOrder.OrderType.TABLE as BibWriter does not have access to preferences
- List> sortOrder = libraryTab.getMainTable().getSortOrder();
- return new SelfContainedSaveOrder(
- SaveOrder.OrderType.SPECIFIED,
- sortOrder.stream()
- .filter(col -> col instanceof MainTableColumn>)
- .map(column -> ((MainTableColumn>) column).getModel())
- .flatMap(model -> model.getSortCriteria().stream())
- .toList());
- } else {
- return SelfContainedSaveOrder.of(so);
- }
- })
- .orElse(SaveOrder.getDefaultSaveOrder());
-
- // Elle configure la sauvegarde, en indiquant qu'aucune sauvegarde supplémentaire (backup) ne doit être créée,
- // que l'ordre de sauvegarde doit être celui défini, et que les entrées doivent être formatées selon les préférences
- // utilisateur.
- SelfContainedSaveConfiguration saveConfiguration = (SelfContainedSaveConfiguration) new SelfContainedSaveConfiguration()
- .withMakeBackup(false)
- .withSaveOrder(saveOrder)
- .withReformatOnSave(preferences.getLibraryPreferences().shouldAlwaysReformatOnSave());
-
- // "Clone" the database context
- // We "know" that "only" the BibEntries might be changed during writing (see [org.jabref.logic.exporter.BibDatabaseWriter.savePartOfDatabase])
- // Chaque entrée BibTeX (comme un article, livre, etc.) est clonée en utilisant la méthode clone().
- // Cela garantit que les modifications faites pendant la sauvegarde n'affecteront pas l'entrée originale.
- List list = bibDatabaseContext.getDatabase().getEntries().stream()
- .map(BibEntry::clone)
- .map(BibEntry.class::cast)
- .toList();
- BibDatabase bibDatabaseClone = new BibDatabase(list);
- BibDatabaseContext bibDatabaseContextClone = new BibDatabaseContext(bibDatabaseClone, bibDatabaseContext.getMetaData());
- // Elle définit l'encodage à utiliser pour écrire le fichier. Cela garantit que les caractères spéciaux sont bien sauvegardés.
- Charset encoding = bibDatabaseContext.getMetaData().getEncoding().orElse(StandardCharsets.UTF_8);
- // We want to have successful backups only
- // Thus, we do not use a plain "FileWriter", but the "AtomicFileWriter"
- // Example: What happens if one hard powers off the machine (or kills the jabref process) during writing of the backup?
- // This MUST NOT create a broken backup file that then jabref wants to "restore" from?
- try (Writer writer = new AtomicFileWriter(backupPath, encoding, false)) {
- BibWriter bibWriter = new BibWriter(writer, bibDatabaseContext.getDatabase().getNewLineSeparator());
- new BibtexDatabaseWriter(
- bibWriter,
- saveConfiguration,
- preferences.getFieldPreferences(),
- preferences.getCitationKeyPatternPreferences(),
- entryTypesManager)
- // we save the clone to prevent the original database (and thus the UI) from being changed
- .saveDatabase(bibDatabaseContextClone);
- backupFilesQueue.add(backupPath);
-
- // We wrote the file successfully
- // Thus, we currently do not need any new backup
- this.needsBackup = false;
- } catch (IOException e) {
- logIfCritical(backupPath, e);
- }
- }
-
- private static Path determineDiscardedFile(Path file, Path backupDir) {
- return backupDir.resolve(BackupFileUtil.getUniqueFilePrefix(file) + "--" + file.getFileName() + "--discarded");
- }
-
- /**
- * Marks the backups as discarded.
- *
- * We do not delete any files, because the user might want to recover old backup files.
- * Therefore, we mark discarded backups by a --discarded file.
- */
- public void discardBackup(Path backupDir) {
- Path path = determineDiscardedFile(bibDatabaseContext.getDatabasePath().get(), backupDir);
- try {
- Files.createFile(path);
- } catch (IOException e) {
- LOGGER.info("Could not create backup file {}", path, e);
- }
- }
-
- private void logIfCritical(Path backupPath, IOException e) {
- Throwable innermostCause = e;
- while (innermostCause.getCause() != null) {
- innermostCause = innermostCause.getCause();
- }
- boolean isErrorInField = innermostCause instanceof InvalidFieldValueException;
-
- // do not print errors in field values into the log during autosave
- if (!isErrorInField) {
- LOGGER.error("Error while saving to file {}", backupPath, e);
- }
- }
-
- @Subscribe
- public synchronized void listen(@SuppressWarnings("unused") BibDatabaseContextChangedEvent event) {
- if (!event.isFilteredOut()) {
- this.needsBackup = true;
- }
- }
-
- private void startBackupTask(Path backupDir) {
- fillQueue(backupDir);
- // remplie backupFilesQueue les files .sav de le meme bibl
-
- executor.scheduleAtFixedRate(
- // We need to determine the backup path on each action, because we use the timestamp in the filename
- () -> determineBackupPathForNewBackup(backupDir).ifPresent(path -> this.performBackup(path)),
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- TimeUnit.SECONDS);
- }
- // La méthode fillQueue(backupDir) est définie dans le code et son rôle est de lister et d'ajouter
-// les fichiers de sauvegarde existants dans une file d'attente,
-
- private void fillQueue(Path backupDir) {
- if (!Files.exists(backupDir)) {
- return;
- }
- bibDatabaseContext.getDatabasePath().ifPresent(databasePath -> {
- // code similar to {@link org.jabref.logic.util.io.BackupFileUtil.getPathOfLatestExisingBackupFile}
- final String prefix = BackupFileUtil.getUniqueFilePrefix(databasePath) + "--" + databasePath.getFileName();
- try {
- List allSavFiles = Files.list(backupDir)
- // just list the .sav belonging to the given targetFile
- .filter(p -> p.getFileName().toString().startsWith(prefix))
- // tous les files .sav commencerait par ce prefix
- .sorted().toList();
- backupFilesQueue.addAll(allSavFiles);
- } catch (IOException e) {
- LOGGER.error("Could not determine most recent file", e);
- }
- });
- }
-
- /**
- * Unregisters the BackupManagerOld from the eventBus of {@link BibDatabaseContext}.
- * This method should only be used when closing a database/JabRef in a normal way.
- *
- * @param backupDir The backup directory
- * @param createBackup If the backup manager should still perform a backup
- */
- private void shutdown(Path backupDir, boolean createBackup) {
- changeFilter.unregisterListener(this);
- changeFilter.shutdown();
- executor.shutdown();
-
- if (createBackup) {
- // Ensure that backup is a recent one
- determineBackupPathForNewBackup(backupDir).ifPresent(this::performBackup);
- }
- }
-}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 9f5be274700..8e1f0227d45 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -13,6 +13,7 @@
import org.jabref.gui.LibraryTab;
import org.jabref.gui.StateManager;
import org.jabref.gui.autosaveandbackup.BackupManager;
+import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.backup.BackupChoiceDialog;
import org.jabref.gui.backup.BackupChoiceDialogRecord;
import org.jabref.gui.backup.BackupResolverDialog;
@@ -38,7 +39,7 @@
import org.slf4j.LoggerFactory;
/**
- * Stores all user dialogs related to {@link BackupManager}.
+ * Stores all user dialogs related to {@link BackupManagerGit}.
*/
public class BackupUIManager {
private static final Logger LOGGER = LoggerFactory.getLogger(BackupUIManager.class);
@@ -84,7 +85,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
/*
return actionOpt.flatMap(action -> {
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
- BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
+ BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
return Optional.empty();
} else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
var test = showBackupChoiceDialog(dialogService, originalPath, preferences);
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index 10487ead6ad..7965a2d626d 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -139,7 +139,7 @@ boolean saveAs(Path file, SaveDatabaseMode mode) {
Optional databasePath = context.getDatabasePath();
if (databasePath.isPresent()) {
- // Close AutosaveManager, BackupManager, and IndexManager for original library
+ // Close AutosaveManager, BackupManagerGit, and IndexManager for original library
AutosaveManager.shutdown(context);
BackupManager.shutdown(context, this.preferences.getFilePreferences().getBackupDirectory(), preferences.getFilePreferences().shouldCreateBackup());
libraryTab.closeIndexManger();
@@ -160,7 +160,7 @@ boolean saveAs(Path file, SaveDatabaseMode mode) {
context.setDatabasePath(file);
libraryTab.updateTabTitle(false);
- // Reset (here: uninstall and install again) AutosaveManager, BackupManager and IndexManager for the new file name
+ // Reset (here: uninstall and install again) AutosaveManager, BackupManagerGit and IndexManager for the new file name
libraryTab.resetChangeMonitor();
libraryTab.installAutosaveManagerAndBackupManager();
libraryTab.createIndexManager();
@@ -251,7 +251,7 @@ private boolean save(Path targetPath, SaveDatabaseMode mode) {
}
private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding, BibDatabaseWriter.SaveType saveType, SelfContainedSaveOrder saveOrder) throws SaveException {
- // if this code is adapted, please also adapt org.jabref.logic.autosaveandbackup.BackupManager.performBackup
+ // if this code is adapted, please also adapt org.jabref.logic.autosaveandbackup.BackupManagerGit.performBackup
SelfContainedSaveConfiguration saveConfiguration
= new SelfContainedSaveConfiguration(saveOrder, false, saveType, preferences.getLibraryPreferences().shouldAlwaysReformatOnSave());
BibDatabaseContext bibDatabaseContext = libraryTab.getBibDatabaseContext();
diff --git a/src/main/java/org/jabref/logic/exporter/SaveConfiguration.java b/src/main/java/org/jabref/logic/exporter/SaveConfiguration.java
index 358659113e5..f92fdf8e8ef 100644
--- a/src/main/java/org/jabref/logic/exporter/SaveConfiguration.java
+++ b/src/main/java/org/jabref/logic/exporter/SaveConfiguration.java
@@ -1,6 +1,6 @@
package org.jabref.logic.exporter;
-import org.jabref.gui.autosaveandbackup.BackupManager;
+import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.model.metadata.SaveOrder;
public class SaveConfiguration {
@@ -44,7 +44,7 @@ public boolean shouldMakeBackup() {
}
/**
- * Required by {@link BackupManager}. Should not be used in other settings
+ * Required by {@link BackupManagerGit}. Should not be used in other settings
*
* @param newMakeBackup whether a backup (.bak file) should be made
*/
diff --git a/src/main/java/org/jabref/logic/util/BackupFileType.java b/src/main/java/org/jabref/logic/util/BackupFileType.java
index b24cc3faeb8..3fbf331daf4 100644
--- a/src/main/java/org/jabref/logic/util/BackupFileType.java
+++ b/src/main/java/org/jabref/logic/util/BackupFileType.java
@@ -5,7 +5,7 @@
public enum BackupFileType implements FileType {
- // Used at BackupManager
+ // Used at BackupManagerGit
BACKUP("Backup", "bak"),
// Used when writing the .bib file. See {@link org.jabref.logic.exporter.AtomicFileWriter}
diff --git a/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java b/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java
index 8df2eb600d7..9b2e1948089 100644
--- a/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java
+++ b/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java
@@ -9,7 +9,7 @@
import java.util.HexFormat;
import java.util.Optional;
-import org.jabref.gui.autosaveandbackup.BackupManager;
+import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.logic.util.BackupFileType;
import org.slf4j.Logger;
@@ -34,7 +34,7 @@ private BackupFileUtil() {
* In case that fails, the return path of the .bak file is set to be next to the .bib file
*
*
- * Note that this backup is different from the .sav
file generated by {@link BackupManager}
+ * Note that this backup is different from the .sav
file generated by {@link BackupManagerGit}
* (and configured in the preferences as "make backups")
*
*/
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
index 653e6f505ca..a45d7cbb9c1 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
@@ -181,7 +181,7 @@ void shouldCreateABackup(@TempDir Path customDir) throws Exception {
fullBackupPath.ifPresent(manager::performBackup);
manager.listen(new GroupUpdatedEvent(new MetaData()));
- BackupManager.shutdown(database, backupDir, true, bibPath);
+ BackupManager.shutdown(database, backupDir, true);
List files = Files.list(backupDir).sorted().toList();
// we only know the first backup path because the second one is created on shutdown
From 9a0707791e84a707824cd193092e84c4ef4989d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Tue, 26 Nov 2024 23:14:30 +0100
Subject: [PATCH 23/84] Start tests
---
.../autosaveandbackup/BackupManagerGit.java | 4 +-
.../BackupManagerGitTest.java | 58 +++++++++++++++++++
2 files changed, 60 insertions(+), 2 deletions(-)
create mode 100644 src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index dae2ed05250..1c980e61e8a 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -71,7 +71,7 @@ public class BackupManagerGit {
if (git.getRepository().getObjectDatabase().exists()) {
LOGGER.info("Git repository already exists");
} else {
- git.init().call();
+ Git.init().call();
LOGGER.info("Initialized new Git repository");
}
}
@@ -144,7 +144,7 @@ private void startBackupTask(Path backupDir, Path originalPath) {
* @throws GitAPIException if a Git API error occurs
*/
- private void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
+ protected void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
/*
needsBackup must be initialized
*/
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
new file mode 100644
index 00000000000..13f6dedaecc
--- /dev/null
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -0,0 +1,58 @@
+package org.jabref.gui.autosaveandbackup;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.jabref.gui.LibraryTab;
+import org.jabref.logic.FilePreferences;
+import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntryTypesManager;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class BackupManagerGitTest {
+
+ private Path backupDir;
+ private BackupManagerGit backupManager;
+
+ @BeforeEach
+ void setup(@TempDir Path tempDir) throws IOException, GitAPIException, IOException {
+ backupDir = tempDir.resolve("backup");
+ Files.createDirectories(backupDir);
+
+ // Initialize BackupManagerGit with mock dependencies
+ var libraryTab = mock(LibraryTab.class);
+ var bibDatabaseContext = new BibDatabaseContext();
+ var entryTypesManager = mock(BibEntryTypesManager.class);
+ var preferences = mock(CliPreferences.class);
+ var filePreferences = mock(FilePreferences.class);
+
+ when(preferences.getFilePreferences()).thenReturn(filePreferences);
+ when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
+
+ backupManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ }
+
+ @Test
+ void testInitialization() {
+ assertNotNull(backupManager);
+ }
+}
+
+
+
+
From 9c86a64e727037b3b8b16766f756327849683f96 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 27 Nov 2024 12:26:58 +0100
Subject: [PATCH 24/84] OpenDatabaseAction adapted
---
.../autosaveandbackup/BackupManagerGit.java | 1 -
.../actions/OpenDatabaseActionGit.java | 329 ++++++++++++++++++
2 files changed, 329 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/org/jabref/gui/importer/actions/OpenDatabaseActionGit.java
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 1c980e61e8a..ffd7db9acf3 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -170,7 +170,6 @@ protected void performBackup(Path backupDir, Path originalPath) throws IOExcepti
* @param objectId the commit ID to restore from
*/
- @SuppressWarnings("checkstyle:TodoComment")
public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
try {
Git git = Git.open(backupDir.toFile());
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseActionGit.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseActionGit.java
new file mode 100644
index 00000000000..b762cb7f81e
--- /dev/null
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseActionGit.java
@@ -0,0 +1,329 @@
+package org.jabref.gui.importer.actions;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.swing.undo.UndoManager;
+
+import org.jabref.gui.ClipBoardManager;
+import org.jabref.gui.DialogService;
+import org.jabref.gui.LibraryTab;
+import org.jabref.gui.LibraryTabContainer;
+import org.jabref.gui.StateManager;
+import org.jabref.gui.actions.SimpleCommand;
+import org.jabref.gui.autosaveandbackup.BackupManagerGit;
+import org.jabref.gui.dialogs.BackupUIManager;
+import org.jabref.gui.preferences.GuiPreferences;
+import org.jabref.gui.shared.SharedDatabaseUIManager;
+import org.jabref.gui.undo.CountingUndoManager;
+import org.jabref.gui.util.FileDialogConfiguration;
+import org.jabref.gui.util.UiTaskExecutor;
+import org.jabref.logic.ai.AiService;
+import org.jabref.logic.importer.OpenDatabase;
+import org.jabref.logic.importer.ParserResult;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.logic.shared.DatabaseNotSupportedException;
+import org.jabref.logic.shared.exception.InvalidDBMSConnectionPropertiesException;
+import org.jabref.logic.shared.exception.NotASharedDatabaseException;
+import org.jabref.logic.util.BackgroundTask;
+import org.jabref.logic.util.Directories;
+import org.jabref.logic.util.StandardFileType;
+import org.jabref.logic.util.TaskExecutor;
+import org.jabref.logic.util.io.FileHistory;
+import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.util.FileUpdateMonitor;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// The action concerned with opening an existing database.
+public class OpenDatabaseActionGit extends SimpleCommand {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(OpenDatabaseAction.class);
+
+ // List of actions that may need to be called after opening the file. Such as
+ // upgrade actions etc. that may depend on the JabRef version that wrote the file:
+ private static final List POST_OPEN_ACTIONS = List.of(
+ // Migrations:
+ // Warning for migrating the Review into the Comment field
+ new MergeReviewIntoCommentAction(),
+ // Check for new custom entry types loaded from the BIB file:
+ new CheckForNewEntryTypesAction(),
+ // Migrate search groups fielded terms to use the new operators (RegEx, case sensitive)
+ new SearchGroupsMigrationAction());
+
+ private final LibraryTabContainer tabContainer;
+ private final GuiPreferences preferences;
+ private final AiService aiService;
+ private final StateManager stateManager;
+ private final FileUpdateMonitor fileUpdateMonitor;
+ private final DialogService dialogService;
+ private final BibEntryTypesManager entryTypesManager;
+ private final CountingUndoManager undoManager;
+ private final ClipBoardManager clipboardManager;
+ private final TaskExecutor taskExecutor;
+
+ public OpenDatabaseActionGit(LibraryTabContainer tabContainer,
+ GuiPreferences preferences,
+ AiService aiService,
+ DialogService dialogService,
+ StateManager stateManager,
+ FileUpdateMonitor fileUpdateMonitor,
+ BibEntryTypesManager entryTypesManager,
+ CountingUndoManager undoManager,
+ ClipBoardManager clipBoardManager,
+ TaskExecutor taskExecutor) {
+ this.tabContainer = tabContainer;
+ this.preferences = preferences;
+ this.aiService = aiService;
+ this.dialogService = dialogService;
+ this.stateManager = stateManager;
+ this.fileUpdateMonitor = fileUpdateMonitor;
+ this.entryTypesManager = entryTypesManager;
+ this.undoManager = undoManager;
+ this.clipboardManager = clipBoardManager;
+ this.taskExecutor = taskExecutor;
+ }
+
+ public static void performPostOpenActions(ParserResult result, DialogService dialogService, CliPreferences preferences) {
+ for (GUIPostOpenAction action : OpenDatabaseActionGit.POST_OPEN_ACTIONS) {
+ if (action.isActionNecessary(result, dialogService, preferences)) {
+ action.performAction(result, dialogService, preferences);
+ }
+ }
+ }
+
+ @Override
+ public void execute() {
+ List filesToOpen = getFilesToOpen();
+ openFiles(new ArrayList<>(filesToOpen));
+ }
+
+ @VisibleForTesting
+ List getFilesToOpen() {
+ List filesToOpen;
+
+ try {
+ FileDialogConfiguration initialDirectoryConfig = getFileDialogConfiguration(getInitialDirectory());
+ filesToOpen = dialogService.showFileOpenDialogAndGetMultipleFiles(initialDirectoryConfig);
+ } catch (IllegalArgumentException e) {
+ // See https://github.com/JabRef/jabref/issues/10548 for details
+ // Rebuild a new config with the home directory
+ FileDialogConfiguration homeDirectoryConfig = getFileDialogConfiguration(Directories.getUserDirectory());
+ filesToOpen = dialogService.showFileOpenDialogAndGetMultipleFiles(homeDirectoryConfig);
+ }
+
+ return filesToOpen;
+ }
+
+ /**
+ * Builds a new FileDialogConfiguration using the given path as the initial directory for use in
+ * dialogService.showFileOpenDialogAndGetMultipleFiles().
+ *
+ * @param initialDirectory Path to use as the initial directory
+ * @return new FileDialogConfig with given initial directory
+ */
+ public FileDialogConfiguration getFileDialogConfiguration(Path initialDirectory) {
+ return new FileDialogConfiguration.Builder()
+ .addExtensionFilter(StandardFileType.BIBTEX_DB)
+ .withDefaultExtension(StandardFileType.BIBTEX_DB)
+ .withInitialDirectory(initialDirectory)
+ .build();
+ }
+
+ /**
+ * @return Path of current panel database directory or the working directory
+ */
+ @VisibleForTesting
+ Path getInitialDirectory() {
+ if (tabContainer.getLibraryTabs().isEmpty()) {
+ return preferences.getFilePreferences().getWorkingDirectory();
+ } else {
+ Optional databasePath = tabContainer.getCurrentLibraryTab().getBibDatabaseContext().getDatabasePath();
+ return databasePath.map(Path::getParent).orElse(preferences.getFilePreferences().getWorkingDirectory());
+ }
+ }
+
+ /**
+ * Opens the given file. If null or 404, nothing happens.
+ * In case the file is already opened, that panel is raised.
+ *
+ * @param file the file, may be null or not existing
+ */
+ public void openFile(Path file) {
+ openFiles(new ArrayList<>(List.of(file)));
+ }
+
+ /**
+ * Opens the given files. If one of it is null or 404, nothing happens.
+ * In case the file is already opened, that panel is raised.
+ *
+ * @param filesToOpen the filesToOpen, may be null or not existing
+ */
+ public void openFiles(List filesToOpen) {
+ LibraryTab toRaise = null;
+ int initialCount = filesToOpen.size();
+ int removed = 0;
+
+ // Check if any of the files are already open:
+ for (Iterator iterator = filesToOpen.iterator(); iterator.hasNext(); ) {
+ Path file = iterator.next();
+ for (LibraryTab libraryTab : tabContainer.getLibraryTabs()) {
+ if ((libraryTab.getBibDatabaseContext().getDatabasePath().isPresent())
+ && libraryTab.getBibDatabaseContext().getDatabasePath().get().equals(file)) {
+ iterator.remove();
+ removed++;
+ // See if we removed the final one. If so, we must perhaps
+ // raise the LibraryTab in question:
+ if (removed == initialCount) {
+ toRaise = libraryTab;
+ }
+ // no more LibraryTabs to check, we found a matching one
+ break;
+ }
+ }
+ }
+
+ // Run the actual open in a thread to prevent the program
+ // locking until the file is loaded.
+ if (!filesToOpen.isEmpty()) {
+ FileHistory fileHistory = preferences.getLastFilesOpenedPreferences().getFileHistory();
+ filesToOpen.forEach(theFile -> {
+ // This method will execute the concrete file opening and loading in a background thread
+ openTheFile(theFile);
+ fileHistory.newFile(theFile);
+ });
+ } else if (toRaise != null && tabContainer.getCurrentLibraryTab() == null) {
+ // If no files are remaining to open, this could mean that a file was
+ // already open. If so, we may have to raise the correct tab:
+ // If there is already a library focused, do not show this library
+ tabContainer.showLibraryTab(toRaise);
+ }
+ }
+
+ /**
+ * This is the real file opening. Should be called via {@link #openFile(Path)}
+ *
+ * Similar method: {@link org.jabref.gui.frame.JabRefFrame#addTab(org.jabref.model.database.BibDatabaseContext, boolean)}.
+ *
+ * @param file the file, may be NOT null, but may not be existing
+ */
+ private void openTheFile(Path file) {
+ Objects.requireNonNull(file);
+ if (!Files.exists(file)) {
+ return;
+ }
+
+ BackgroundTask backgroundTask = BackgroundTask.wrap(() -> loadDatabase(file));
+ // The backgroundTask is executed within the method createLibraryTab
+ LibraryTab newTab = LibraryTab.createLibraryTab(
+ backgroundTask,
+ file,
+ dialogService,
+ aiService,
+ preferences,
+ stateManager,
+ tabContainer,
+ fileUpdateMonitor,
+ entryTypesManager,
+ undoManager,
+ clipboardManager,
+ taskExecutor);
+ tabContainer.addTab(newTab, true);
+ }
+
+ private ParserResult loadDatabase(Path file) throws Exception {
+ Path fileToLoad = file.toAbsolutePath();
+
+ dialogService.notify(Localization.lang("Opening") + ": '" + file + "'");
+
+ preferences.getFilePreferences().setWorkingDirectory(fileToLoad.getParent());
+ Path backupDir = preferences.getFilePreferences().getBackupDirectory();
+
+ ParserResult parserResult = null;
+ if (BackupManagerGit.backupGitDiffers(fileToLoad, backupDir)) {
+ // In case the backup differs, ask the user what to do.
+ // In case the user opted for restoring a backup, the content of the backup is contained in parserResult.
+ parserResult = BackupUIManager.showRestoreBackupDialog(dialogService, fileToLoad, preferences, fileUpdateMonitor, undoManager, stateManager)
+ .orElse(null);
+ }
+
+ try {
+ if (parserResult == null) {
+ // No backup was restored, do the "normal" loading
+ parserResult = OpenDatabase.loadDatabase(fileToLoad,
+ preferences.getImportFormatPreferences(),
+ fileUpdateMonitor);
+ }
+
+ if (parserResult.hasWarnings()) {
+ String content = Localization.lang("Please check your library file for wrong syntax.")
+ + "\n\n" + parserResult.getErrorMessage();
+ UiTaskExecutor.runInJavaFXThread(() ->
+ dialogService.showWarningDialogAndWait(Localization.lang("Open library error"), content));
+ }
+ } catch (IOException e) {
+ parserResult = ParserResult.fromError(e);
+ LOGGER.error("Error opening file '{}'", fileToLoad, e);
+ }
+
+ if (parserResult.getDatabase().isShared()) {
+ openSharedDatabase(
+ parserResult,
+ tabContainer,
+ dialogService,
+ preferences,
+ aiService,
+ stateManager,
+ entryTypesManager,
+ fileUpdateMonitor,
+ undoManager,
+ clipboardManager,
+ taskExecutor);
+ }
+ return parserResult;
+ }
+
+ public static void openSharedDatabase(ParserResult parserResult,
+ LibraryTabContainer tabContainer,
+ DialogService dialogService,
+ GuiPreferences preferences,
+ AiService aiService,
+ StateManager stateManager,
+ BibEntryTypesManager entryTypesManager,
+ FileUpdateMonitor fileUpdateMonitor,
+ UndoManager undoManager,
+ ClipBoardManager clipBoardManager,
+ TaskExecutor taskExecutor)
+ throws SQLException, DatabaseNotSupportedException, InvalidDBMSConnectionPropertiesException, NotASharedDatabaseException {
+ try {
+ new SharedDatabaseUIManager(
+ tabContainer,
+ dialogService,
+ preferences,
+ aiService,
+ stateManager,
+ entryTypesManager,
+ fileUpdateMonitor,
+ undoManager,
+ clipBoardManager,
+ taskExecutor)
+ .openSharedDatabaseFromParserResult(parserResult);
+ } catch (SQLException | DatabaseNotSupportedException | InvalidDBMSConnectionPropertiesException |
+ NotASharedDatabaseException e) {
+ parserResult.getDatabaseContext().clearDatabasePath(); // do not open the original file
+ parserResult.getDatabase().clearSharedDatabaseID();
+
+ throw e;
+ }
+ }
+}
From fa42e866e455b45216d01d96d65029cfea4385f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 27 Nov 2024 14:22:13 +0100
Subject: [PATCH 25/84] Start adapting SaveDatabaseAction.java and
LibraryTab.java
---
src/main/java/org/jabref/gui/LibraryTab.java | 3 ++-
.../org/jabref/gui/autosaveandbackup/BackupManagerGit.java | 2 +-
src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java | 3 ++-
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index f7579709894..2a5dd6c5d30 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -43,6 +43,7 @@
import org.jabref.gui.autocompleter.SuggestionProviders;
import org.jabref.gui.autosaveandbackup.AutosaveManager;
import org.jabref.gui.autosaveandbackup.BackupManager;
+import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.collab.DatabaseChangeMonitor;
import org.jabref.gui.dialogs.AutosaveUiManager;
import org.jabref.gui.entryeditor.EntryEditor;
@@ -373,7 +374,7 @@ public void installAutosaveManagerAndBackupManager() {
autosaveManager.registerListener(new AutosaveUiManager(this, dialogService, preferences, entryTypesManager));
}
if (isDatabaseReadyForBackup(bibDatabaseContext) && preferences.getFilePreferences().shouldCreateBackup()) {
- BackupManager.start(this, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class), preferences);
+ BackupManagerGit.start(this, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class), preferences);
}
}
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index ffd7db9acf3..bea8a3980c5 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -91,7 +91,7 @@ public class BackupManagerGit {
public BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
BackupManagerGit BackupManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- BackupManager.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
+ this.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
runningInstances.add(BackupManager);
return BackupManager;
}
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index 7965a2d626d..0d0376b86de 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -22,6 +22,7 @@
import org.jabref.gui.LibraryTab;
import org.jabref.gui.autosaveandbackup.AutosaveManager;
import org.jabref.gui.autosaveandbackup.BackupManager;
+import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.maintable.BibEntryTableViewModel;
import org.jabref.gui.maintable.columns.MainTableColumn;
import org.jabref.gui.preferences.GuiPreferences;
@@ -141,7 +142,7 @@ boolean saveAs(Path file, SaveDatabaseMode mode) {
if (databasePath.isPresent()) {
// Close AutosaveManager, BackupManagerGit, and IndexManager for original library
AutosaveManager.shutdown(context);
- BackupManager.shutdown(context, this.preferences.getFilePreferences().getBackupDirectory(), preferences.getFilePreferences().shouldCreateBackup());
+ BackupManagerGit.shutdown(context, this.preferences.getFilePreferences().getBackupDirectory(), preferences.getFilePreferences().shouldCreateBackup(), databasePath.get());
libraryTab.closeIndexManger();
}
From be482cd1cfecc721ebeb00411434ef31a3e28695 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 27 Nov 2024 15:12:09 +0100
Subject: [PATCH 26/84] Classes adapted, except for UI
---
src/main/java/org/jabref/gui/LibraryTab.java | 19 +-
.../autosaveandbackup/BackupManagerGit.java | 10 +-
.../gui/exporter/SaveDatabaseAction.java | 26 +-
.../importer/actions/OpenDatabaseAction.java | 46 +--
.../actions/OpenDatabaseActionGit.java | 329 ------------------
.../gui/exporter/SaveDatabaseActionTest.java | 7 +-
6 files changed, 67 insertions(+), 370 deletions(-)
delete mode 100644 src/main/java/org/jabref/gui/importer/actions/OpenDatabaseActionGit.java
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index 2a5dd6c5d30..86227248bb0 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -106,6 +106,7 @@
import com.tobiasdiez.easybind.Subscription;
import org.controlsfx.control.NotificationPane;
import org.controlsfx.control.action.Action;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -309,7 +310,7 @@ private void onDatabaseLoadingStarted() {
getMainTable().placeholderProperty().setValue(loadingLayout);
}
- private void onDatabaseLoadingSucceed(ParserResult result) {
+ private void onDatabaseLoadingSucceed(ParserResult result) throws GitAPIException, IOException {
OpenDatabaseAction.performPostOpenActions(result, dialogService, preferences);
if (result.getChangedOnMigration()) {
this.markBaseChanged();
@@ -344,7 +345,7 @@ private void onDatabaseLoadingFailed(Exception ex) {
dialogService.showErrorDialogAndWait(title, content, ex);
}
- private void setDatabaseContext(BibDatabaseContext bibDatabaseContext) {
+ private void setDatabaseContext(BibDatabaseContext bibDatabaseContext) throws GitAPIException, IOException {
TabPane tabPane = this.getTabPane();
if (tabPane == null) {
LOGGER.debug("User interrupted loading. Not showing any library.");
@@ -368,13 +369,13 @@ private void setDatabaseContext(BibDatabaseContext bibDatabaseContext) {
installAutosaveManagerAndBackupManager();
}
- public void installAutosaveManagerAndBackupManager() {
+ public void installAutosaveManagerAndBackupManager() throws GitAPIException, IOException {
if (isDatabaseReadyForAutoSave(bibDatabaseContext)) {
AutosaveManager autosaveManager = AutosaveManager.start(bibDatabaseContext);
autosaveManager.registerListener(new AutosaveUiManager(this, dialogService, preferences, entryTypesManager));
}
if (isDatabaseReadyForBackup(bibDatabaseContext) && preferences.getFilePreferences().shouldCreateBackup()) {
- BackupManagerGit.start(this, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class), preferences);
+ BackupManagerGit.start(this, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class), preferences, bibDatabaseContext.getDatabasePath().get());
}
}
@@ -1083,7 +1084,15 @@ public static LibraryTab createLibraryTab(BackgroundTask dataLoadi
newTab.setDataLoadingTask(dataLoadingTask);
dataLoadingTask.onRunning(newTab::onDatabaseLoadingStarted)
- .onSuccess(newTab::onDatabaseLoadingSucceed)
+ .onSuccess(result -> {
+ try {
+ newTab.onDatabaseLoadingSucceed(result);
+ } catch (Exception e) {
+ // We need to handle the exception.
+ // Handle the exception, e.g., log it or show an error dialog
+ LOGGER.error("An error occurred while loading the database", e);
+ }
+ })
.onFailure(newTab::onDatabaseLoadingFailed)
.executeWith(taskExecutor);
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index bea8a3980c5..cb623bf48d1 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -89,11 +89,11 @@ public class BackupManagerGit {
* @throws GitAPIException if a Git API error occurs
*/
- public BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
- BackupManagerGit BackupManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- this.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
- runningInstances.add(BackupManager);
- return BackupManager;
+ public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
+ BackupManagerGit backupManagerGit = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
+ runningInstances.add(backupManagerGit);
+ return backupManagerGit;
}
/**
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index 0d0376b86de..afefbd69318 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -21,7 +21,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.LibraryTab;
import org.jabref.gui.autosaveandbackup.AutosaveManager;
-import org.jabref.gui.autosaveandbackup.BackupManager;
import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.maintable.BibEntryTableViewModel;
import org.jabref.gui.maintable.columns.MainTableColumn;
@@ -45,6 +44,7 @@
import org.jabref.model.metadata.SaveOrder;
import org.jabref.model.metadata.SelfContainedSaveOrder;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -88,11 +88,18 @@ public boolean save(SaveDatabaseMode mode) {
/**
* Asks the user for the path and saves afterward
*/
+
public void saveAs() {
- askForSavePath().ifPresent(this::saveAs);
+ askForSavePath().ifPresent(path -> {
+ try {
+ saveAs(path);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to save the database", e);
+ }
+ });
}
- public boolean saveAs(Path file) {
+ public boolean saveAs(Path file) throws GitAPIException, IOException {
return this.saveAs(file, SaveDatabaseMode.NORMAL);
}
@@ -135,7 +142,7 @@ public void saveSelectedAsPlain() {
* successful save.
* @return true on successful save
*/
- boolean saveAs(Path file, SaveDatabaseMode mode) {
+ boolean saveAs(Path file, SaveDatabaseMode mode) throws GitAPIException, IOException {
BibDatabaseContext context = libraryTab.getBibDatabaseContext();
Optional databasePath = context.getDatabasePath();
@@ -205,7 +212,16 @@ private boolean save(BibDatabaseContext bibDatabaseContext, SaveDatabaseMode mod
Optional databasePath = bibDatabaseContext.getDatabasePath();
if (databasePath.isEmpty()) {
Optional savePath = askForSavePath();
- return savePath.filter(path -> saveAs(path, mode)).isPresent();
+ return savePath.filter(path -> {
+ try {
+ return saveAs(path, mode);
+ } catch (
+ GitAPIException |
+ IOException e) {
+ LOGGER.error("A problem occurred when trying to save the file %s".formatted(path), e);
+ throw new RuntimeException(e);
+ }
+ }).isPresent();
}
return save(databasePath.get(), mode);
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
index 748c78db227..2c3436e2476 100644
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
@@ -18,7 +18,7 @@
import org.jabref.gui.LibraryTabContainer;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;
-import org.jabref.gui.autosaveandbackup.BackupManager;
+import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.dialogs.BackupUIManager;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.shared.SharedDatabaseUIManager;
@@ -73,15 +73,15 @@ public class OpenDatabaseAction extends SimpleCommand {
private final TaskExecutor taskExecutor;
public OpenDatabaseAction(LibraryTabContainer tabContainer,
- GuiPreferences preferences,
- AiService aiService,
- DialogService dialogService,
- StateManager stateManager,
- FileUpdateMonitor fileUpdateMonitor,
- BibEntryTypesManager entryTypesManager,
- CountingUndoManager undoManager,
- ClipBoardManager clipBoardManager,
- TaskExecutor taskExecutor) {
+ GuiPreferences preferences,
+ AiService aiService,
+ DialogService dialogService,
+ StateManager stateManager,
+ FileUpdateMonitor fileUpdateMonitor,
+ BibEntryTypesManager entryTypesManager,
+ CountingUndoManager undoManager,
+ ClipBoardManager clipBoardManager,
+ TaskExecutor taskExecutor) {
this.tabContainer = tabContainer;
this.preferences = preferences;
this.aiService = aiService;
@@ -250,7 +250,7 @@ private ParserResult loadDatabase(Path file) throws Exception {
Path backupDir = preferences.getFilePreferences().getBackupDirectory();
ParserResult parserResult = null;
- if (BackupManager.backupFileDiffers(fileToLoad, backupDir)) {
+ if (BackupManagerGit.backupGitDiffers(fileToLoad, backupDir)) {
// In case the backup differs, ask the user what to do.
// In case the user opted for restoring a backup, the content of the backup is contained in parserResult.
parserResult = BackupUIManager.showRestoreBackupDialog(dialogService, fileToLoad, preferences, fileUpdateMonitor, undoManager, stateManager)
@@ -277,18 +277,18 @@ private ParserResult loadDatabase(Path file) throws Exception {
}
if (parserResult.getDatabase().isShared()) {
- openSharedDatabase(
- parserResult,
- tabContainer,
- dialogService,
- preferences,
- aiService,
- stateManager,
- entryTypesManager,
- fileUpdateMonitor,
- undoManager,
- clipboardManager,
- taskExecutor);
+ openSharedDatabase(
+ parserResult,
+ tabContainer,
+ dialogService,
+ preferences,
+ aiService,
+ stateManager,
+ entryTypesManager,
+ fileUpdateMonitor,
+ undoManager,
+ clipboardManager,
+ taskExecutor);
}
return parserResult;
}
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseActionGit.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseActionGit.java
deleted file mode 100644
index b762cb7f81e..00000000000
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseActionGit.java
+++ /dev/null
@@ -1,329 +0,0 @@
-package org.jabref.gui.importer.actions;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-import javax.swing.undo.UndoManager;
-
-import org.jabref.gui.ClipBoardManager;
-import org.jabref.gui.DialogService;
-import org.jabref.gui.LibraryTab;
-import org.jabref.gui.LibraryTabContainer;
-import org.jabref.gui.StateManager;
-import org.jabref.gui.actions.SimpleCommand;
-import org.jabref.gui.autosaveandbackup.BackupManagerGit;
-import org.jabref.gui.dialogs.BackupUIManager;
-import org.jabref.gui.preferences.GuiPreferences;
-import org.jabref.gui.shared.SharedDatabaseUIManager;
-import org.jabref.gui.undo.CountingUndoManager;
-import org.jabref.gui.util.FileDialogConfiguration;
-import org.jabref.gui.util.UiTaskExecutor;
-import org.jabref.logic.ai.AiService;
-import org.jabref.logic.importer.OpenDatabase;
-import org.jabref.logic.importer.ParserResult;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.shared.DatabaseNotSupportedException;
-import org.jabref.logic.shared.exception.InvalidDBMSConnectionPropertiesException;
-import org.jabref.logic.shared.exception.NotASharedDatabaseException;
-import org.jabref.logic.util.BackgroundTask;
-import org.jabref.logic.util.Directories;
-import org.jabref.logic.util.StandardFileType;
-import org.jabref.logic.util.TaskExecutor;
-import org.jabref.logic.util.io.FileHistory;
-import org.jabref.model.entry.BibEntryTypesManager;
-import org.jabref.model.util.FileUpdateMonitor;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-// The action concerned with opening an existing database.
-public class OpenDatabaseActionGit extends SimpleCommand {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(OpenDatabaseAction.class);
-
- // List of actions that may need to be called after opening the file. Such as
- // upgrade actions etc. that may depend on the JabRef version that wrote the file:
- private static final List POST_OPEN_ACTIONS = List.of(
- // Migrations:
- // Warning for migrating the Review into the Comment field
- new MergeReviewIntoCommentAction(),
- // Check for new custom entry types loaded from the BIB file:
- new CheckForNewEntryTypesAction(),
- // Migrate search groups fielded terms to use the new operators (RegEx, case sensitive)
- new SearchGroupsMigrationAction());
-
- private final LibraryTabContainer tabContainer;
- private final GuiPreferences preferences;
- private final AiService aiService;
- private final StateManager stateManager;
- private final FileUpdateMonitor fileUpdateMonitor;
- private final DialogService dialogService;
- private final BibEntryTypesManager entryTypesManager;
- private final CountingUndoManager undoManager;
- private final ClipBoardManager clipboardManager;
- private final TaskExecutor taskExecutor;
-
- public OpenDatabaseActionGit(LibraryTabContainer tabContainer,
- GuiPreferences preferences,
- AiService aiService,
- DialogService dialogService,
- StateManager stateManager,
- FileUpdateMonitor fileUpdateMonitor,
- BibEntryTypesManager entryTypesManager,
- CountingUndoManager undoManager,
- ClipBoardManager clipBoardManager,
- TaskExecutor taskExecutor) {
- this.tabContainer = tabContainer;
- this.preferences = preferences;
- this.aiService = aiService;
- this.dialogService = dialogService;
- this.stateManager = stateManager;
- this.fileUpdateMonitor = fileUpdateMonitor;
- this.entryTypesManager = entryTypesManager;
- this.undoManager = undoManager;
- this.clipboardManager = clipBoardManager;
- this.taskExecutor = taskExecutor;
- }
-
- public static void performPostOpenActions(ParserResult result, DialogService dialogService, CliPreferences preferences) {
- for (GUIPostOpenAction action : OpenDatabaseActionGit.POST_OPEN_ACTIONS) {
- if (action.isActionNecessary(result, dialogService, preferences)) {
- action.performAction(result, dialogService, preferences);
- }
- }
- }
-
- @Override
- public void execute() {
- List filesToOpen = getFilesToOpen();
- openFiles(new ArrayList<>(filesToOpen));
- }
-
- @VisibleForTesting
- List getFilesToOpen() {
- List filesToOpen;
-
- try {
- FileDialogConfiguration initialDirectoryConfig = getFileDialogConfiguration(getInitialDirectory());
- filesToOpen = dialogService.showFileOpenDialogAndGetMultipleFiles(initialDirectoryConfig);
- } catch (IllegalArgumentException e) {
- // See https://github.com/JabRef/jabref/issues/10548 for details
- // Rebuild a new config with the home directory
- FileDialogConfiguration homeDirectoryConfig = getFileDialogConfiguration(Directories.getUserDirectory());
- filesToOpen = dialogService.showFileOpenDialogAndGetMultipleFiles(homeDirectoryConfig);
- }
-
- return filesToOpen;
- }
-
- /**
- * Builds a new FileDialogConfiguration using the given path as the initial directory for use in
- * dialogService.showFileOpenDialogAndGetMultipleFiles().
- *
- * @param initialDirectory Path to use as the initial directory
- * @return new FileDialogConfig with given initial directory
- */
- public FileDialogConfiguration getFileDialogConfiguration(Path initialDirectory) {
- return new FileDialogConfiguration.Builder()
- .addExtensionFilter(StandardFileType.BIBTEX_DB)
- .withDefaultExtension(StandardFileType.BIBTEX_DB)
- .withInitialDirectory(initialDirectory)
- .build();
- }
-
- /**
- * @return Path of current panel database directory or the working directory
- */
- @VisibleForTesting
- Path getInitialDirectory() {
- if (tabContainer.getLibraryTabs().isEmpty()) {
- return preferences.getFilePreferences().getWorkingDirectory();
- } else {
- Optional databasePath = tabContainer.getCurrentLibraryTab().getBibDatabaseContext().getDatabasePath();
- return databasePath.map(Path::getParent).orElse(preferences.getFilePreferences().getWorkingDirectory());
- }
- }
-
- /**
- * Opens the given file. If null or 404, nothing happens.
- * In case the file is already opened, that panel is raised.
- *
- * @param file the file, may be null or not existing
- */
- public void openFile(Path file) {
- openFiles(new ArrayList<>(List.of(file)));
- }
-
- /**
- * Opens the given files. If one of it is null or 404, nothing happens.
- * In case the file is already opened, that panel is raised.
- *
- * @param filesToOpen the filesToOpen, may be null or not existing
- */
- public void openFiles(List filesToOpen) {
- LibraryTab toRaise = null;
- int initialCount = filesToOpen.size();
- int removed = 0;
-
- // Check if any of the files are already open:
- for (Iterator iterator = filesToOpen.iterator(); iterator.hasNext(); ) {
- Path file = iterator.next();
- for (LibraryTab libraryTab : tabContainer.getLibraryTabs()) {
- if ((libraryTab.getBibDatabaseContext().getDatabasePath().isPresent())
- && libraryTab.getBibDatabaseContext().getDatabasePath().get().equals(file)) {
- iterator.remove();
- removed++;
- // See if we removed the final one. If so, we must perhaps
- // raise the LibraryTab in question:
- if (removed == initialCount) {
- toRaise = libraryTab;
- }
- // no more LibraryTabs to check, we found a matching one
- break;
- }
- }
- }
-
- // Run the actual open in a thread to prevent the program
- // locking until the file is loaded.
- if (!filesToOpen.isEmpty()) {
- FileHistory fileHistory = preferences.getLastFilesOpenedPreferences().getFileHistory();
- filesToOpen.forEach(theFile -> {
- // This method will execute the concrete file opening and loading in a background thread
- openTheFile(theFile);
- fileHistory.newFile(theFile);
- });
- } else if (toRaise != null && tabContainer.getCurrentLibraryTab() == null) {
- // If no files are remaining to open, this could mean that a file was
- // already open. If so, we may have to raise the correct tab:
- // If there is already a library focused, do not show this library
- tabContainer.showLibraryTab(toRaise);
- }
- }
-
- /**
- * This is the real file opening. Should be called via {@link #openFile(Path)}
- *
- * Similar method: {@link org.jabref.gui.frame.JabRefFrame#addTab(org.jabref.model.database.BibDatabaseContext, boolean)}.
- *
- * @param file the file, may be NOT null, but may not be existing
- */
- private void openTheFile(Path file) {
- Objects.requireNonNull(file);
- if (!Files.exists(file)) {
- return;
- }
-
- BackgroundTask backgroundTask = BackgroundTask.wrap(() -> loadDatabase(file));
- // The backgroundTask is executed within the method createLibraryTab
- LibraryTab newTab = LibraryTab.createLibraryTab(
- backgroundTask,
- file,
- dialogService,
- aiService,
- preferences,
- stateManager,
- tabContainer,
- fileUpdateMonitor,
- entryTypesManager,
- undoManager,
- clipboardManager,
- taskExecutor);
- tabContainer.addTab(newTab, true);
- }
-
- private ParserResult loadDatabase(Path file) throws Exception {
- Path fileToLoad = file.toAbsolutePath();
-
- dialogService.notify(Localization.lang("Opening") + ": '" + file + "'");
-
- preferences.getFilePreferences().setWorkingDirectory(fileToLoad.getParent());
- Path backupDir = preferences.getFilePreferences().getBackupDirectory();
-
- ParserResult parserResult = null;
- if (BackupManagerGit.backupGitDiffers(fileToLoad, backupDir)) {
- // In case the backup differs, ask the user what to do.
- // In case the user opted for restoring a backup, the content of the backup is contained in parserResult.
- parserResult = BackupUIManager.showRestoreBackupDialog(dialogService, fileToLoad, preferences, fileUpdateMonitor, undoManager, stateManager)
- .orElse(null);
- }
-
- try {
- if (parserResult == null) {
- // No backup was restored, do the "normal" loading
- parserResult = OpenDatabase.loadDatabase(fileToLoad,
- preferences.getImportFormatPreferences(),
- fileUpdateMonitor);
- }
-
- if (parserResult.hasWarnings()) {
- String content = Localization.lang("Please check your library file for wrong syntax.")
- + "\n\n" + parserResult.getErrorMessage();
- UiTaskExecutor.runInJavaFXThread(() ->
- dialogService.showWarningDialogAndWait(Localization.lang("Open library error"), content));
- }
- } catch (IOException e) {
- parserResult = ParserResult.fromError(e);
- LOGGER.error("Error opening file '{}'", fileToLoad, e);
- }
-
- if (parserResult.getDatabase().isShared()) {
- openSharedDatabase(
- parserResult,
- tabContainer,
- dialogService,
- preferences,
- aiService,
- stateManager,
- entryTypesManager,
- fileUpdateMonitor,
- undoManager,
- clipboardManager,
- taskExecutor);
- }
- return parserResult;
- }
-
- public static void openSharedDatabase(ParserResult parserResult,
- LibraryTabContainer tabContainer,
- DialogService dialogService,
- GuiPreferences preferences,
- AiService aiService,
- StateManager stateManager,
- BibEntryTypesManager entryTypesManager,
- FileUpdateMonitor fileUpdateMonitor,
- UndoManager undoManager,
- ClipBoardManager clipBoardManager,
- TaskExecutor taskExecutor)
- throws SQLException, DatabaseNotSupportedException, InvalidDBMSConnectionPropertiesException, NotASharedDatabaseException {
- try {
- new SharedDatabaseUIManager(
- tabContainer,
- dialogService,
- preferences,
- aiService,
- stateManager,
- entryTypesManager,
- fileUpdateMonitor,
- undoManager,
- clipBoardManager,
- taskExecutor)
- .openSharedDatabaseFromParserResult(parserResult);
- } catch (SQLException | DatabaseNotSupportedException | InvalidDBMSConnectionPropertiesException |
- NotASharedDatabaseException e) {
- parserResult.getDatabaseContext().clearDatabasePath(); // do not open the original file
- parserResult.getDatabase().clearSharedDatabaseID();
-
- throw e;
- }
- }
-}
diff --git a/src/test/java/org/jabref/gui/exporter/SaveDatabaseActionTest.java b/src/test/java/org/jabref/gui/exporter/SaveDatabaseActionTest.java
index bbc91aa2501..8f5f80c3f22 100644
--- a/src/test/java/org/jabref/gui/exporter/SaveDatabaseActionTest.java
+++ b/src/test/java/org/jabref/gui/exporter/SaveDatabaseActionTest.java
@@ -33,6 +33,7 @@
import org.jabref.model.metadata.MetaData;
import org.jabref.model.metadata.SaveOrder;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -67,7 +68,7 @@ void setUp() {
}
@Test
- void saveAsShouldSetWorkingDirectory() {
+ void saveAsShouldSetWorkingDirectory() throws GitAPIException, IOException {
when(dialogService.showFileSaveDialog(any(FileDialogConfiguration.class))).thenReturn(Optional.of(file));
doReturn(true).when(saveDatabaseAction).saveAs(any());
@@ -77,7 +78,7 @@ void saveAsShouldSetWorkingDirectory() {
}
@Test
- void saveAsShouldNotSetWorkingDirectoryIfNotSelected() {
+ void saveAsShouldNotSetWorkingDirectoryIfNotSelected() throws GitAPIException, IOException {
when(dialogService.showFileSaveDialog(any(FileDialogConfiguration.class))).thenReturn(Optional.empty());
doReturn(false).when(saveDatabaseAction).saveAs(any());
@@ -87,7 +88,7 @@ void saveAsShouldNotSetWorkingDirectoryIfNotSelected() {
}
@Test
- void saveShouldShowSaveAsIfDatabaseNotSelected() {
+ void saveShouldShowSaveAsIfDatabaseNotSelected() throws GitAPIException, IOException {
when(dbContext.getDatabasePath()).thenReturn(Optional.empty());
when(dbContext.getLocation()).thenReturn(DatabaseLocation.LOCAL);
when(dialogService.showFileSaveDialog(any())).thenReturn(Optional.of(file));
From 2797c01887261301b3c23f402d6e11d15c1d1c04 Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Wed, 27 Nov 2024 16:00:30 +0100
Subject: [PATCH 27/84] UI update
---
.../jabref/gui/backup/BackupChoiceDialog.java | 40 ++++++-------------
1 file changed, 13 insertions(+), 27 deletions(-)
diff --git a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
index 8049e5888a6..435ac24c63f 100644
--- a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
+++ b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
@@ -8,7 +8,6 @@
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
-import javafx.scene.control.Pagination;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
@@ -21,7 +20,7 @@ public class BackupChoiceDialog extends BaseDialog {
public static final ButtonType IGNORE_BACKUP = new ButtonType(Localization.lang("Ignore backup"), ButtonBar.ButtonData.CANCEL_CLOSE);
public static final ButtonType REVIEW_BACKUP = new ButtonType(Localization.lang("Review backup"), ButtonBar.ButtonData.LEFT);
- private static final int ROWS_PER_PAGE = 10; // Define number of rows per page
+ private final ObservableList tableData = FXCollections.observableArrayList();
@FXML
private final TableView backupTableView;
@@ -32,38 +31,20 @@ public BackupChoiceDialog(Path originalPath, Path backupDir) {
getDialogPane().setMinHeight(150);
getDialogPane().setMinWidth(450);
getDialogPane().getButtonTypes().setAll(RESTORE_BACKUP, IGNORE_BACKUP, REVIEW_BACKUP);
+
String content = Localization.lang("It looks like JabRef did not shut down cleanly last time the file was used.") + "\n\n" +
Localization.lang("Do you want to recover the library from a backup file?");
- backupTableView = new TableView();
+ backupTableView = new TableView<>();
setupBackupTableView();
+ pushSampleData();
+ backupTableView.setItems(tableData);
- // Sample data
- ObservableList data = FXCollections.observableArrayList();
- for (int i = 0; i < 100; i++) { // Adjust 20 to however many entries you want
- data.add(new BackupEntry("2023-11-01", i + " MB", i));
- }
- setContentText(content);
-
- // Pagination control
- int pageCount = (int) Math.ceil(data.size() / (double) ROWS_PER_PAGE);
- Pagination pagination = new Pagination(pageCount, 0);
- pagination.setPageFactory(pageIndex -> {
- int start = pageIndex * ROWS_PER_PAGE;
- int end = Math.min(start + ROWS_PER_PAGE, data.size());
- backupTableView.setItems(FXCollections.observableArrayList(data.subList(start, end)));
- backupTableView.getSelectionModel().selectFirst();
- return new VBox(backupTableView);
- });
-
- // VBox content to hold the pagination and the label
- VBox contentBox = new VBox(10);
- contentBox.getChildren().addAll(new Label(content), pagination);
+ VBox contentBox = new VBox();
+ contentBox.getChildren().addAll(new Label(content), backupTableView);
contentBox.setPrefWidth(380);
- // Set the dialog content
getDialogPane().setContent(contentBox);
-
setResultConverter(dialogButton -> {
if (dialogButton == RESTORE_BACKUP || dialogButton == REVIEW_BACKUP) {
return new BackupChoiceDialogRecord(backupTableView.getSelectionModel().getSelectedItem(), dialogButton);
@@ -84,5 +65,10 @@ private void setupBackupTableView() {
backupTableView.getColumns().addAll(dateColumn, sizeColumn, entriesColumn);
}
-}
+ private void pushSampleData() {
+ for (int i = 0; i < 50; i++) {
+ tableData.add(new BackupEntry("2023-11-" + (i + 1), i + " MB", i));
+ }
+ }
+}
From d070e67ac380ddb9ad81da7483707835b6be32e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 27 Nov 2024 16:20:17 +0100
Subject: [PATCH 28/84] Starting tests
---
src/main/java/org/jabref/gui/LibraryTab.java | 2 +-
.../BackupManagerGitTest.java | 107 +++++++++---
.../autosaveandbackup/BackupManagerTest.java | 160 ------------------
3 files changed, 89 insertions(+), 180 deletions(-)
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index 86227248bb0..1cd6dfaaeef 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -375,7 +375,7 @@ public void installAutosaveManagerAndBackupManager() throws GitAPIException, IOE
autosaveManager.registerListener(new AutosaveUiManager(this, dialogService, preferences, entryTypesManager));
}
if (isDatabaseReadyForBackup(bibDatabaseContext) && preferences.getFilePreferences().shouldCreateBackup()) {
- BackupManagerGit.start(this, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class), preferences, bibDatabaseContext.getDatabasePath().get());
+ BackupManagerGit.start(this, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class), preferences, bibDatabaseContext.getDatabasePath().get().getParent());
}
}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 13f6dedaecc..7d2e4812625 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -3,53 +3,122 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.List;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.FileTime;
import org.jabref.gui.LibraryTab;
import org.jabref.logic.FilePreferences;
import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.logic.util.BackupFileType;
+import org.jabref.logic.util.io.BackupFileUtil;
+import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;
import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class BackupManagerGitTest {
- private Path backupDir;
- private BackupManagerGit backupManager;
+ Path backupDir;
@BeforeEach
- void setup(@TempDir Path tempDir) throws IOException, GitAPIException, IOException {
+ void setup(@TempDir Path tempDir) throws IOException, GitAPIException {
backupDir = tempDir.resolve("backup");
- Files.createDirectories(backupDir);
+ }
+
+ @Test
+ void testGitRepositoryExistsAfterInitialization() throws IOException, GitAPIException {
+ // Initialize BackupManagerGit
+ BackupManagerGit backupManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+
+ // Check if the .git directory exists
+ Path gitDir = backupDir.resolve(".git");
+ assertTrue(Files.exists(gitDir), "Git repository should exist after initialization");
+ }
+
+ @Test
+ void testBackupFileIsEqualForNonExistingBackup() throws Exception {
+ Path originalFile = Path.of(BackupManagerGitTest.class.getResource("no-autosave.bib").toURI());
+ assertFalse(BackupManagerGit.backupGitDiffers(originalFile, backupDir));
+ }
+
+ @Test
+ void testBackupFileIsEqual() throws Exception {
+ // Prepare test: Create backup file on "right" path
+ Path source = Path.of(BackupManagerGitTest.class.getResource("no-changes.bib.bak").toURI());
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerGitTest.class.getResource("no-changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
+ Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+
+ Path originalFile = Path.of(BackupManagerGitTest.class.getResource("no-changes.bib").toURI());
+ assertFalse(BackupManagerGit.backupGitDiffers(originalFile, backupDir));
+ }
+
+ @Test
+ void testBackupFileDiffers() throws Exception {
+ // Prepare test: Create backup file on "right" path
+ Path source = Path.of(BackupManagerGitTest.class.getResource("changes.bib.bak").toURI());
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
+ Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+
+ Path originalFile = Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI());
+ assertTrue(BackupManagerGit.backupGitDiffers(originalFile, backupDir));
+ }
- // Initialize BackupManagerGit with mock dependencies
- var libraryTab = mock(LibraryTab.class);
- var bibDatabaseContext = new BibDatabaseContext();
- var entryTypesManager = mock(BibEntryTypesManager.class);
- var preferences = mock(CliPreferences.class);
- var filePreferences = mock(FilePreferences.class);
+ @Test
+ void testCorrectBackupFileDeterminedForMultipleBakFiles() throws Exception {
+ Path noChangesBib = Path.of(BackupManagerGitTest.class.getResource("no-changes.bib").toURI());
+ Path noChangesBibBak = Path.of(BackupManagerGitTest.class.getResource("no-changes.bib.bak").toURI());
- when(preferences.getFilePreferences()).thenReturn(filePreferences);
- when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
+ // Prepare test: Create backup files on "right" path
+ // most recent file does not have any changes
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(noChangesBib, BackupFileType.BACKUP, backupDir);
+ Files.copy(noChangesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
- backupManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ // create "older" .bak files containing changes
+ for (int i = 0; i < 10; i++) {
+ Path changesBibBak = Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI());
+ Path directory = backupDir;
+ String timeSuffix = "2020-02-03--00.00.0" + Integer.toString(i);
+ String fileName = BackupFileUtil.getUniqueFilePrefix(noChangesBib) + "--no-changes.bib--" + timeSuffix + ".bak";
+ target = directory.resolve(fileName);
+ Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+ Path originalFile = noChangesBib;
+ assertFalse(BackupManagerGit.backupGitDiffers(originalFile, backupDir));
}
@Test
- void testInitialization() {
- assertNotNull(backupManager);
+ void testBakFileWithNewerTimeStampLeadsToDiff() throws Exception {
+ Path changesBib = Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI());
+ Path changesBibBak = Path.of(BackupManagerGitTest.class.getResource("changes.bib.bak").toURI());
+
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
+ Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
+
+ assertTrue(BackupManagerGit.backupGitDiffers(changesBib, backupDir));
+ }
+
+ @Test
+ void testBakFileWithOlderTimeStampDoesNotLeadToDiff() throws Exception {
+ Path changesBib = Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI());
+ Path changesBibBak = Path.of(BackupManagerGitTest.class.getResource("changes.bib.bak").toURI());
+
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
+ Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
+
+ // Make .bak file very old
+ Files.setLastModifiedTime(target, FileTime.fromMillis(0));
+
+ assertFalse(BackupManagerGit.backupGitDiffers(changesBib, backupDir));
}
}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
index a45d7cbb9c1..4fe048d4a86 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
@@ -1,37 +1,14 @@
package org.jabref.gui.autosaveandbackup;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.FileTime;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import org.jabref.gui.LibraryTab;
-import org.jabref.logic.FilePreferences;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.util.BackupFileType;
import org.jabref.logic.util.Directories;
-import org.jabref.logic.util.io.BackupFileUtil;
-import org.jabref.model.database.BibDatabase;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntryTypesManager;
-import org.jabref.model.groups.event.GroupUpdatedEvent;
-import org.jabref.model.metadata.MetaData;
-import org.jabref.model.metadata.event.MetaDataChangedEvent;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import org.mockito.Answers;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
class BackupManagerTest {
@@ -51,141 +28,4 @@ void backupFileNameIsCorrectlyGeneratedInAppDataDirectory() {
// Pattern is "27182d3c--test.bib--", but the hashing is implemented differently on Linux than on Windows
assertNotEquals("", bakPath);
}
-
- @Test
- void backupFileIsEqualForNonExistingBackup() throws Exception {
- Path originalFile = Path.of(BackupManagerTest.class.getResource("no-autosave.bib").toURI());
- assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
- }
-
- @Test
- void backupFileIsEqual() throws Exception {
- // Prepare test: Create backup file on "right" path
- Path source = Path.of(BackupManagerTest.class.getResource("no-changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
- Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
-
- Path originalFile = Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI());
- assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
- }
-
- @Test
- void backupFileDiffers() throws Exception {
- // Prepare test: Create backup file on "right" path
- Path source = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
- Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
-
- Path originalFile = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- assertTrue(BackupManager.backupFileDiffers(originalFile, backupDir));
- }
-
- @Test
- void correctBackupFileDeterminedForMultipleBakFiles() throws Exception {
- Path noChangesBib = Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI());
- Path noChangesBibBak = Path.of(BackupManagerTest.class.getResource("no-changes.bib.bak").toURI());
-
- // Prepare test: Create backup files on "right" path
- // most recent file does not have any changes
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(noChangesBib, BackupFileType.BACKUP, backupDir);
- Files.copy(noChangesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
-
- // create "older" .bak files containing changes
- for (int i = 0; i < 10; i++) {
- Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- Path directory = backupDir;
- String timeSuffix = "2020-02-03--00.00.0" + Integer.toString(i);
- String fileName = BackupFileUtil.getUniqueFilePrefix(noChangesBib) + "--no-changes.bib--" + timeSuffix + ".bak";
- target = directory.resolve(fileName);
- Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
- }
-
- Path originalFile = noChangesBib;
- assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
- }
-
- @Test
- void bakFileWithNewerTimeStampLeadsToDiff() throws Exception {
- Path changesBib = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
-
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
- Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
-
- assertTrue(BackupManager.backupFileDiffers(changesBib, backupDir));
- }
-
- @Test
- void bakFileWithOlderTimeStampDoesNotLeadToDiff() throws Exception {
- Path changesBib = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
-
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
- Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
-
- // Make .bak file very old
- Files.setLastModifiedTime(target, FileTime.fromMillis(0));
-
- assertFalse(BackupManager.backupFileDiffers(changesBib, backupDir));
- }
-
- @Test
- void shouldNotCreateABackup(@TempDir Path customDir) throws Exception {
- Path backupDir = customDir.resolve("subBackupDir");
- Files.createDirectories(backupDir);
-
- var database = new BibDatabaseContext(new BibDatabase());
- database.setDatabasePath(customDir.resolve("Bibfile.bib"));
-
- var preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
- var filePreferences = mock(FilePreferences.class);
- when(preferences.getFilePreferences()).thenReturn(filePreferences);
- when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
- when(filePreferences.shouldCreateBackup()).thenReturn(false);
-
- BackupManager manager = BackupManager.start(
- mock(LibraryTab.class),
- database,
- mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS),
- preferences);
- manager.listen(new MetaDataChangedEvent(new MetaData()));
-
- BackupManager.shutdown(database, filePreferences.getBackupDirectory(), filePreferences.shouldCreateBackup());
-
- List files = Files.list(backupDir).toList();
- assertEquals(Collections.emptyList(), files);
- }
-
- @Test
- void shouldCreateABackup(@TempDir Path customDir) throws Exception {
- Path backupDir = customDir.resolve("subBackupDir");
- Files.createDirectories(backupDir);
-
- var database = new BibDatabaseContext(new BibDatabase());
- database.setDatabasePath(customDir.resolve("Bibfile.bib"));
-
- var preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
- var filePreferences = mock(FilePreferences.class);
- when(preferences.getFilePreferences()).thenReturn(filePreferences);
- when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
- when(filePreferences.shouldCreateBackup()).thenReturn(true);
-
- BackupManager manager = BackupManager.start(
- mock(LibraryTab.class),
- database,
- mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS),
- preferences);
- manager.listen(new MetaDataChangedEvent(new MetaData()));
-
- Optional fullBackupPath = manager.determineBackupPathForNewBackup(backupDir);
- fullBackupPath.ifPresent(manager::performBackup);
- manager.listen(new GroupUpdatedEvent(new MetaData()));
-
- BackupManager.shutdown(database, backupDir, true);
-
- List files = Files.list(backupDir).sorted().toList();
- // we only know the first backup path because the second one is created on shutdown
- // due to timing issues we cannot test that reliable
- assertEquals(fullBackupPath.get(), files.getFirst());
- }
}
From 528fe039c87d9f32051183ce87eb6211c03730c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 27 Nov 2024 16:22:04 +0100
Subject: [PATCH 29/84] Starting tests
---
.../BackupManagerGitTest.java | 105 +-----------
.../autosaveandbackup/BackupManagerTest.java | 160 ++++++++++++++++++
2 files changed, 161 insertions(+), 104 deletions(-)
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 7d2e4812625..a98b4c96f75 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -1,30 +1,12 @@
package org.jabref.gui.autosaveandbackup;
import java.io.IOException;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.FileTime;
-
-import org.jabref.gui.LibraryTab;
-import org.jabref.logic.FilePreferences;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.util.BackupFileType;
-import org.jabref.logic.util.io.BackupFileUtil;
-import org.jabref.model.database.BibDatabase;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntryTypesManager;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
class BackupManagerGitTest {
Path backupDir;
@@ -33,94 +15,9 @@ class BackupManagerGitTest {
void setup(@TempDir Path tempDir) throws IOException, GitAPIException {
backupDir = tempDir.resolve("backup");
}
+}
- @Test
- void testGitRepositoryExistsAfterInitialization() throws IOException, GitAPIException {
- // Initialize BackupManagerGit
- BackupManagerGit backupManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
-
- // Check if the .git directory exists
- Path gitDir = backupDir.resolve(".git");
- assertTrue(Files.exists(gitDir), "Git repository should exist after initialization");
- }
-
- @Test
- void testBackupFileIsEqualForNonExistingBackup() throws Exception {
- Path originalFile = Path.of(BackupManagerGitTest.class.getResource("no-autosave.bib").toURI());
- assertFalse(BackupManagerGit.backupGitDiffers(originalFile, backupDir));
- }
-
- @Test
- void testBackupFileIsEqual() throws Exception {
- // Prepare test: Create backup file on "right" path
- Path source = Path.of(BackupManagerGitTest.class.getResource("no-changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerGitTest.class.getResource("no-changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
- Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
-
- Path originalFile = Path.of(BackupManagerGitTest.class.getResource("no-changes.bib").toURI());
- assertFalse(BackupManagerGit.backupGitDiffers(originalFile, backupDir));
- }
-
- @Test
- void testBackupFileDiffers() throws Exception {
- // Prepare test: Create backup file on "right" path
- Path source = Path.of(BackupManagerGitTest.class.getResource("changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
- Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
-
- Path originalFile = Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI());
- assertTrue(BackupManagerGit.backupGitDiffers(originalFile, backupDir));
- }
-
- @Test
- void testCorrectBackupFileDeterminedForMultipleBakFiles() throws Exception {
- Path noChangesBib = Path.of(BackupManagerGitTest.class.getResource("no-changes.bib").toURI());
- Path noChangesBibBak = Path.of(BackupManagerGitTest.class.getResource("no-changes.bib.bak").toURI());
-
- // Prepare test: Create backup files on "right" path
- // most recent file does not have any changes
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(noChangesBib, BackupFileType.BACKUP, backupDir);
- Files.copy(noChangesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
-
- // create "older" .bak files containing changes
- for (int i = 0; i < 10; i++) {
- Path changesBibBak = Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI());
- Path directory = backupDir;
- String timeSuffix = "2020-02-03--00.00.0" + Integer.toString(i);
- String fileName = BackupFileUtil.getUniqueFilePrefix(noChangesBib) + "--no-changes.bib--" + timeSuffix + ".bak";
- target = directory.resolve(fileName);
- Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
- }
-
- Path originalFile = noChangesBib;
- assertFalse(BackupManagerGit.backupGitDiffers(originalFile, backupDir));
- }
-
- @Test
- void testBakFileWithNewerTimeStampLeadsToDiff() throws Exception {
- Path changesBib = Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI());
- Path changesBibBak = Path.of(BackupManagerGitTest.class.getResource("changes.bib.bak").toURI());
-
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
- Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
-
- assertTrue(BackupManagerGit.backupGitDiffers(changesBib, backupDir));
- }
-
- @Test
- void testBakFileWithOlderTimeStampDoesNotLeadToDiff() throws Exception {
- Path changesBib = Path.of(BackupManagerGitTest.class.getResource("changes.bib").toURI());
- Path changesBibBak = Path.of(BackupManagerGitTest.class.getResource("changes.bib.bak").toURI());
-
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
- Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
-
- // Make .bak file very old
- Files.setLastModifiedTime(target, FileTime.fromMillis(0));
- assertFalse(BackupManagerGit.backupGitDiffers(changesBib, backupDir));
- }
-}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
index 4fe048d4a86..a45d7cbb9c1 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
@@ -1,14 +1,37 @@
package org.jabref.gui.autosaveandbackup;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.FileTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import org.jabref.gui.LibraryTab;
+import org.jabref.logic.FilePreferences;
+import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.logic.util.BackupFileType;
import org.jabref.logic.util.Directories;
+import org.jabref.logic.util.io.BackupFileUtil;
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.groups.event.GroupUpdatedEvent;
+import org.jabref.model.metadata.MetaData;
+import org.jabref.model.metadata.event.MetaDataChangedEvent;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Answers;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
class BackupManagerTest {
@@ -28,4 +51,141 @@ void backupFileNameIsCorrectlyGeneratedInAppDataDirectory() {
// Pattern is "27182d3c--test.bib--", but the hashing is implemented differently on Linux than on Windows
assertNotEquals("", bakPath);
}
+
+ @Test
+ void backupFileIsEqualForNonExistingBackup() throws Exception {
+ Path originalFile = Path.of(BackupManagerTest.class.getResource("no-autosave.bib").toURI());
+ assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
+ }
+
+ @Test
+ void backupFileIsEqual() throws Exception {
+ // Prepare test: Create backup file on "right" path
+ Path source = Path.of(BackupManagerTest.class.getResource("no-changes.bib.bak").toURI());
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
+ Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+
+ Path originalFile = Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI());
+ assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
+ }
+
+ @Test
+ void backupFileDiffers() throws Exception {
+ // Prepare test: Create backup file on "right" path
+ Path source = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
+ Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+
+ Path originalFile = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
+ assertTrue(BackupManager.backupFileDiffers(originalFile, backupDir));
+ }
+
+ @Test
+ void correctBackupFileDeterminedForMultipleBakFiles() throws Exception {
+ Path noChangesBib = Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI());
+ Path noChangesBibBak = Path.of(BackupManagerTest.class.getResource("no-changes.bib.bak").toURI());
+
+ // Prepare test: Create backup files on "right" path
+ // most recent file does not have any changes
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(noChangesBib, BackupFileType.BACKUP, backupDir);
+ Files.copy(noChangesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
+
+ // create "older" .bak files containing changes
+ for (int i = 0; i < 10; i++) {
+ Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
+ Path directory = backupDir;
+ String timeSuffix = "2020-02-03--00.00.0" + Integer.toString(i);
+ String fileName = BackupFileUtil.getUniqueFilePrefix(noChangesBib) + "--no-changes.bib--" + timeSuffix + ".bak";
+ target = directory.resolve(fileName);
+ Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+ Path originalFile = noChangesBib;
+ assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
+ }
+
+ @Test
+ void bakFileWithNewerTimeStampLeadsToDiff() throws Exception {
+ Path changesBib = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
+ Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
+
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
+ Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
+
+ assertTrue(BackupManager.backupFileDiffers(changesBib, backupDir));
+ }
+
+ @Test
+ void bakFileWithOlderTimeStampDoesNotLeadToDiff() throws Exception {
+ Path changesBib = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
+ Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
+
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
+ Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
+
+ // Make .bak file very old
+ Files.setLastModifiedTime(target, FileTime.fromMillis(0));
+
+ assertFalse(BackupManager.backupFileDiffers(changesBib, backupDir));
+ }
+
+ @Test
+ void shouldNotCreateABackup(@TempDir Path customDir) throws Exception {
+ Path backupDir = customDir.resolve("subBackupDir");
+ Files.createDirectories(backupDir);
+
+ var database = new BibDatabaseContext(new BibDatabase());
+ database.setDatabasePath(customDir.resolve("Bibfile.bib"));
+
+ var preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
+ var filePreferences = mock(FilePreferences.class);
+ when(preferences.getFilePreferences()).thenReturn(filePreferences);
+ when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
+ when(filePreferences.shouldCreateBackup()).thenReturn(false);
+
+ BackupManager manager = BackupManager.start(
+ mock(LibraryTab.class),
+ database,
+ mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS),
+ preferences);
+ manager.listen(new MetaDataChangedEvent(new MetaData()));
+
+ BackupManager.shutdown(database, filePreferences.getBackupDirectory(), filePreferences.shouldCreateBackup());
+
+ List files = Files.list(backupDir).toList();
+ assertEquals(Collections.emptyList(), files);
+ }
+
+ @Test
+ void shouldCreateABackup(@TempDir Path customDir) throws Exception {
+ Path backupDir = customDir.resolve("subBackupDir");
+ Files.createDirectories(backupDir);
+
+ var database = new BibDatabaseContext(new BibDatabase());
+ database.setDatabasePath(customDir.resolve("Bibfile.bib"));
+
+ var preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
+ var filePreferences = mock(FilePreferences.class);
+ when(preferences.getFilePreferences()).thenReturn(filePreferences);
+ when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
+ when(filePreferences.shouldCreateBackup()).thenReturn(true);
+
+ BackupManager manager = BackupManager.start(
+ mock(LibraryTab.class),
+ database,
+ mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS),
+ preferences);
+ manager.listen(new MetaDataChangedEvent(new MetaData()));
+
+ Optional fullBackupPath = manager.determineBackupPathForNewBackup(backupDir);
+ fullBackupPath.ifPresent(manager::performBackup);
+ manager.listen(new GroupUpdatedEvent(new MetaData()));
+
+ BackupManager.shutdown(database, backupDir, true);
+
+ List files = Files.list(backupDir).sorted().toList();
+ // we only know the first backup path because the second one is created on shutdown
+ // due to timing issues we cannot test that reliable
+ assertEquals(fullBackupPath.get(), files.getFirst());
+ }
}
From 11f65c78174c4d596112ef3e66cbf2f6affb21cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 28 Nov 2024 00:01:37 +0100
Subject: [PATCH 30/84] first tests that are working
---
.../autosaveandbackup/BackupManagerGit.java | 70 +++++---
.../BackupManagerGitTest.java | 149 +++++++++++++++++-
2 files changed, 194 insertions(+), 25 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index cb623bf48d1..aa76fb52ac6 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -4,6 +4,8 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Date;
@@ -23,6 +25,7 @@
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
@@ -30,7 +33,6 @@
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,20 +64,33 @@ public class BackupManagerGit {
changeFilter = new CoarseChangeFilter(bibDatabaseContext);
changeFilter.registerListener(this);
+ // Ensure the backup directory exists
+ File backupDir = preferences.getFilePreferences().getBackupDirectory().toFile();
+ if (!backupDir.exists()) {
+ boolean dirCreated = backupDir.mkdirs();
+ if (dirCreated) {
+ LOGGER.info("Created backup directory: " + backupDir);
+ } else {
+ LOGGER.error("Failed to create backup directory: " + backupDir);
+ }
+ }
+
// Initialize Git repository
FileRepositoryBuilder builder = new FileRepositoryBuilder();
- git = new Git(builder.setGitDir(new File(preferences.getFilePreferences().getBackupDirectory().toFile(), ".git"))
+ git = new Git(builder.setGitDir(new File(backupDir, ".git"))
.readEnvironment()
.findGitDir()
.build());
+
if (git.getRepository().getObjectDatabase().exists()) {
LOGGER.info("Git repository already exists");
} else {
- Git.init().call();
+ Git.init().setDirectory(backupDir).call(); // Explicitly set the directory
LOGGER.info("Initialized new Git repository");
}
}
+
/**
* Starts a new BackupManagerGit instance and begins the backup task.
*
@@ -107,7 +122,7 @@ public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext b
@SuppressWarnings({"checkstyle:NoWhitespaceBefore", "checkstyle:WhitespaceAfter"})
public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup, Path originalPath) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownJGit(backupDir, createBackup, originalPath));
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownGit(backupDir, createBackup, originalPath));
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
}
@@ -145,9 +160,8 @@ private void startBackupTask(Path backupDir, Path originalPath) {
*/
protected void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
- /*
- needsBackup must be initialized
- */
+ LOGGER.info("Starting backup process for file: {}", originalPath);
+
needsBackup = BackupManagerGit.backupGitDiffers(backupDir, originalPath);
if (!needsBackup) {
return;
@@ -198,25 +212,42 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
-
public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
-
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
.setGitDir(new File(repoDir, ".git"))
.build();
+
try (Git git = new Git(repository)) {
- ObjectId headCommitId = repository.resolve("HEAD"); // to get the latest commit id
+ // Resolve HEAD commit
+ ObjectId headCommitId = repository.resolve("HEAD");
if (headCommitId == null) {
- // No commits in the repository, so there's no previous backup
- return false;
+ // No commits in the repository; assume the file differs
+ return true;
}
- git.add().addFilepattern(originalPath.getFileName().toString()).call();
- String relativePath = backupDir.relativize(originalPath).toString();
- List diffs = git.diff()
- .setPathFilter(PathFilter.create(relativePath)) // Utiliser PathFilter ici
- .call();
- return !diffs.isEmpty();
+
+ // Attempt to retrieve the file content from the last commit
+ ObjectLoader loader;
+ try {
+ loader = repository.open(repository.resolve("HEAD:" + originalPath.getFileName().toString()));
+ } catch (
+ MissingObjectException e) {
+ // File not found in the last commit; assume it differs
+ return true;
+ }
+
+ // Read the content from the last commit
+ String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
+
+ // Read the current content of the file
+ if (!Files.exists(originalPath)) {
+ // If the file doesn't exist in the working directory, it differs
+ return true;
+ }
+ String currentContent = Files.readString(originalPath, StandardCharsets.UTF_8);
+
+ // Compare the current content to the committed content
+ return !currentContent.equals(committedContent);
}
}
@@ -230,7 +261,6 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
-
public List showDiffers(Path originalPath, Path backupDir, String commitId) throws IOException, GitAPIException {
File repoDir = backupDir.toFile();
@@ -347,7 +377,7 @@ public List> retrieveCommitDetails(List commits, Path ba
* @param originalPath the original path of the file to be backed up
*/
- private void shutdownJGit(Path backupDir, boolean createBackup, Path originalPath) {
+ private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
executor.shutdown();
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index a98b4c96f75..6f826bd6214 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -1,19 +1,157 @@
package org.jabref.gui.autosaveandbackup;
-import java.io.IOException;
-import java.nio.file.Path;
-
-import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.Git;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Answers;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+import org.jabref.gui.LibraryTab;
+import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntryTypesManager;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
class BackupManagerGitTest {
+ @TempDir
+ Path tempDir;
Path backupDir;
+ Git git;
+ CliPreferences preferences;
+ BibEntryTypesManager entryTypesManager;
+ LibraryTab libraryTab;
+ BibDatabaseContext bibDatabaseContext;
@BeforeEach
- void setup(@TempDir Path tempDir) throws IOException, GitAPIException {
+ void setup() throws Exception {
backupDir = tempDir.resolve("backup");
+ Files.createDirectories(backupDir);
+
+ // Initialize a mock Git repository in the parent directory of the backup directory
+ git = Git.init().setDirectory(backupDir.getParent().toFile()).call();
+
+ // Mock dependencies
+ preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
+ entryTypesManager = mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS);
+ libraryTab = mock(LibraryTab.class);
+
+ when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDir);
+ }
+
+ @Test
+ void testBackupManagerInitializesGitRepository() throws Exception {
+ // Ensure the backup directory exists
+ Path backupDir = tempDir.resolve("backup");
+ Files.createDirectories(backupDir);
+
+ // Create a BibDatabaseContext
+ Path databaseFile = tempDir.resolve("test.bib");
+ Files.writeString(databaseFile, "Initial content");
+ var bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
+ bibDatabaseContext.setDatabasePath(databaseFile);
+
+ // Initialize BackupManagerGit
+ BackupManagerGit manager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+
+ // Ensure the Git repository is initialized in the backup directory
+ assertTrue(Files.exists(backupDir.resolve(".git")), "Git repository not initialized in backup directory");
+ }
+
+ @Test
+ void testBackupGitDiffers_NoDifferences() throws Exception {
+ // Create a file in the original directory
+ Path originalFile = tempDir.resolve("test.bib");
+ Files.writeString(originalFile, "Initial content");
+
+ // Create the backup directory if it doesn't exist
+ Files.createDirectories(backupDir);
+
+ // Copy the original file to the backup directory
+ Path fileInBackupDir = backupDir.resolve("test.bib");
+ Files.copy(originalFile, fileInBackupDir, StandardCopyOption.REPLACE_EXISTING);
+
+ // Initialize the Git repository if not already done
+ if (!Files.exists(backupDir.resolve(".git"))) {
+ Git.init().setDirectory(backupDir.toFile()).call();
+ }
+
+ // Add and commit the file to the Git repository
+ Git git = Git.open(backupDir.toFile());
+ git.add().addFilepattern("test.bib").call();
+ git.commit().setMessage("Initial commit").call();
+
+ // Check that no differences are detected between the backup file and Git repository
+ boolean differs = BackupManagerGit.backupGitDiffers(fileInBackupDir, backupDir);
+ assertFalse(differs, "Differences were detected when there should be none.");
+
+ // Clean up resources
+ BackupManagerGit.shutdown(bibDatabaseContext, backupDir, false, originalFile);
+ }
+
+ @Test
+ void testBackupGitDiffers_WithDifferences() throws Exception {
+ // Create a file in the backup directory
+ Path originalFile = tempDir.resolve("test.bib");
+ Files.writeString(originalFile, "Initial content");
+
+ // Copy the file to the backup directory
+ Path fileInBackupDir = backupDir.resolve("test.bib");
+ Files.copy(originalFile, fileInBackupDir, StandardCopyOption.REPLACE_EXISTING);
+
+ // Add and commit the file in the Git repository
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("Initial commit").call();
+
+ // Modify the file in the backup directory
+ Files.writeString(fileInBackupDir, "Modified content");
+
+ // Check that differences are detected
+ boolean differs = BackupManagerGit.backupGitDiffers(fileInBackupDir, backupDir);
+ assertTrue(differs);
+
+ BackupManagerGit.shutdown(bibDatabaseContext, backupDir, differs, originalFile);
+ }
+
+ @Test
+ void testNoNewRepositoryCreated() throws Exception {
+ // Create a fake file to simulate the database file
+ Path databaseFile = tempDir.resolve("test.bib");
+ Files.writeString(databaseFile, "Initial content");
+
+ // Set up BibDatabaseContext with the file path
+ var bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
+ bibDatabaseContext.setDatabasePath(databaseFile);
+
+ // Ensure the initial repository is created
+ BackupManagerGit initialManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ assertTrue(Files.exists(backupDir.resolve(".git"))); // Ensure the repo exists
+
+ // Use backupGitDiffers to check if the backup differs
+ boolean createBackup;
+ if (bibDatabaseContext.getDatabasePath().isPresent()) {
+ createBackup = BackupManagerGit.backupGitDiffers(bibDatabaseContext.getDatabasePath().get(), backupDir);
+ } else {
+ fail("Database path is not present");
+ return; // Avoid further execution if the path is missing
+ }
+
+ // Shutdown the initial manager
+ BackupManagerGit.shutdown(bibDatabaseContext, backupDir, createBackup, bibDatabaseContext.getDatabasePath().get());
+
+ // Create another instance pointing to the same backup directory
+ BackupManagerGit newManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ assertTrue(Files.exists(backupDir.resolve(".git"))); // Ensure no new repo is created
+
+ // Shutdown the new manager
+ BackupManagerGit.shutdown(bibDatabaseContext, backupDir, createBackup, bibDatabaseContext.getDatabasePath().get());
}
}
@@ -22,3 +160,4 @@ void setup(@TempDir Path tempDir) throws IOException, GitAPIException {
+
From 8ebb62aebbf953c30972b6e3a01b86358284a55d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 28 Nov 2024 01:19:18 +0100
Subject: [PATCH 31/84] All the tests for BackupManagerGit + Launcher working
---
.../autosaveandbackup/BackupManagerGit.java | 46 ++--
.../BackupManagerGitTest.java | 260 +++++++++++++++++-
2 files changed, 278 insertions(+), 28 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index aa76fb52ac6..a7f46276a25 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -8,6 +8,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
@@ -90,7 +91,6 @@ public class BackupManagerGit {
}
}
-
/**
* Starts a new BackupManagerGit instance and begins the backup task.
*
@@ -133,8 +133,7 @@ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDi
* @param originalPath the original path of the file to be backed up
*/
- @SuppressWarnings({"checkstyle:WhitespaceAfter", "checkstyle:LeftCurly"})
- private void startBackupTask(Path backupDir, Path originalPath) {
+ void startBackupTask(Path backupDir, Path originalPath) {
executor.scheduleAtFixedRate(
() -> {
try {
@@ -212,6 +211,7 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
+
public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
@@ -261,6 +261,7 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
+
public List showDiffers(Path originalPath, Path backupDir, String commitId) throws IOException, GitAPIException {
File repoDir = backupDir.toFile();
@@ -280,43 +281,41 @@ need a class to show the last ten backups indicating: date/ size/ number of entr
return diffFr.scan(oldCommit, newCommit);
}
- // n is a counter incrementing by 1 when the user asks to see older versions (packs of 10)
-// and decrements by 1 when the user asks to see the pack of the 10 earlier versions
-// the scroll down: n->n+1 ; the scroll up: n->n-1
- public List retreiveCommits(Path backupDir, int n) throws IOException, GitAPIException {
+ /**
+ * Retrieves the last n commits from the Git repository.
+ *
+ * @param backupDir the backup directory
+ * @param n the number of commits to retrieve
+ * @return a list of RevCommit objects representing the commits
+ * @throws IOException if an I/O error occurs
+ * @throws GitAPIException if a Git API error occurs
+ */
+
+ public List retrieveCommits(Path backupDir, int n) throws IOException, GitAPIException {
List retrievedCommits = new ArrayList<>();
- // Open Git depository
+ // Open Git repository
try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- // Use RevWalk to go through all commits
+ // Use RevWalk to traverse commits
try (RevWalk revWalk = new RevWalk(repository)) {
- // Start from HEAD
RevCommit startCommit = revWalk.parseCommit(repository.resolve("HEAD"));
revWalk.markStart(startCommit);
int count = 0;
- int startIndex = n * 10;
- int endIndex = startIndex + 9;
-
for (RevCommit commit : revWalk) {
- // Ignore commits before starting index
- if (count < startIndex) {
- count++;
- continue;
- }
- if (count >= endIndex) {
- break;
- }
- // Add commits to the main list
retrievedCommits.add(commit);
count++;
+ if (count == n) {
+ break; // Stop after collecting the required number of commits
+ }
}
}
}
+ // Reverse the list to have commits in the correct order
+ Collections.reverse(retrievedCommits);
return retrievedCommits;
}
-
/**
* Retrieves detailed information about the specified commits.
*
@@ -327,7 +326,6 @@ public List retreiveCommits(Path backupDir, int n) throws IOException
* @throws GitAPIException if a Git API error occurs
*/
- @SuppressWarnings("checkstyle:WhitespaceAround")
public List> retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
List> commitDetails;
try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 6f826bd6214..34d384c3ea1 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -1,14 +1,22 @@
package org.jabref.gui.autosaveandbackup;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Answers;
+import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.StreamSupport;
import org.jabref.gui.LibraryTab;
import org.jabref.logic.preferences.CliPreferences;
@@ -16,8 +24,13 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
class BackupManagerGitTest {
@@ -35,8 +48,8 @@ void setup() throws Exception {
backupDir = tempDir.resolve("backup");
Files.createDirectories(backupDir);
- // Initialize a mock Git repository in the parent directory of the backup directory
- git = Git.init().setDirectory(backupDir.getParent().toFile()).call();
+ // Initialize the Git repository inside backupDir
+ git = Git.init().setDirectory(backupDir.toFile()).call();
// Mock dependencies
preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
@@ -153,6 +166,245 @@ void testNoNewRepositoryCreated() throws Exception {
// Shutdown the new manager
BackupManagerGit.shutdown(bibDatabaseContext, backupDir, createBackup, bibDatabaseContext.getDatabasePath().get());
}
+
+ @Test
+ void testStartMethod() throws Exception {
+ // Arrange: Set up necessary dependencies and mock objects
+ Path databaseFile = tempDir.resolve("test.bib");
+ Files.writeString(databaseFile, "Initial content");
+
+ BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
+ bibDatabaseContext.setDatabasePath(databaseFile);
+
+ Path backupDirectory = tempDir.resolve("backup");
+ Files.createDirectories(backupDirectory);
+
+ // Mock preferences to return the backup directory
+ when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDirectory);
+
+ // Act: Call the start method
+ BackupManagerGit backupManager = BackupManagerGit.start(
+ libraryTab,
+ bibDatabaseContext,
+ entryTypesManager,
+ preferences,
+ databaseFile
+ );
+
+ // Assert: Verify the outcomes
+ // Ensure a Git repository is initialized in the backup directory
+ assertTrue(Files.exists(backupDirectory.resolve(".git")), "Git repository not initialized");
+
+ // Use reflection to access the private `runningInstances`
+ Field runningInstancesField = BackupManagerGit.class.getDeclaredField("runningInstances");
+ runningInstancesField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Set runningInstances = (Set) runningInstancesField.get(null);
+
+ // Ensure the backup manager is added to the running instances
+ assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
+
+ // Clean up by shutting down the backup manager
+ BackupManagerGit.shutdown(bibDatabaseContext, backupDirectory, false, databaseFile);
+ }
+
+ @Test
+ void testStartBackupTaskWithReflection() throws Exception {
+ // Arrange: Similar setup as above
+ Path databaseFile = tempDir.resolve("test.bib");
+ Files.writeString(databaseFile, "Initial content");
+
+ BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
+ bibDatabaseContext.setDatabasePath(databaseFile);
+
+ Path backupDirectory = tempDir.resolve("backup");
+ Files.createDirectories(backupDirectory);
+
+ when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDirectory);
+
+ BackupManagerGit backupManager = BackupManagerGit.start(
+ libraryTab,
+ bibDatabaseContext,
+ entryTypesManager,
+ preferences,
+ databaseFile
+ );
+
+ // Act: Start the backup task
+ // private void startBackupTask(Path backupDir, Path originalPath)
+ backupManager.startBackupTask(backupDirectory, databaseFile);
+
+ // Simulate passage of time
+ Thread.sleep(100);
+
+ // Use reflection to access the private `runningInstances`
+ Field runningInstancesField = BackupManagerGit.class.getDeclaredField("runningInstances");
+ runningInstancesField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Set runningInstances = (Set) runningInstancesField.get(null);
+
+ // Assert: Verify the backup task is active
+ assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
+
+ // Clean up
+ BackupManagerGit.shutdown(bibDatabaseContext, backupDirectory, false, databaseFile);
+ }
+
+ @Test
+ void testRestoreBackup() throws Exception {
+ // Create multiple commits
+ ObjectId targetCommitId = null;
+ for (int i = 1; i <= 3; i++) {
+ Path file = backupDir.resolve("file" + i + ".txt");
+ Files.writeString(file, "Content of file " + i);
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Commit " + i).call();
+ if (i == 2) {
+ // Save the ID of the second commit for testing
+ targetCommitId = commit.getId();
+ }
+ }
+
+ // Act: Call restoreBackup
+ BackupManagerGit.restoreBackup(tempDir.resolve("restored.txt"), backupDir, targetCommitId);
+
+ // Assert: Verify the repository has a new commit after restoration
+ try (RevWalk revWalk = new RevWalk(git.getRepository())) {
+ RevCommit headCommit = revWalk.parseCommit(git.getRepository().resolve("HEAD"));
+ assertTrue(
+ headCommit.getShortMessage().contains("Restored content from commit: " + targetCommitId.getName()),
+ "A new commit should indicate the restoration"
+ );
+ }
+
+ // Assert: Ensure the file from the restored commit exists
+ assertTrue(
+ Files.exists(backupDir.resolve("file2.txt")),
+ "File from the restored commit should be present"
+ );
+
+ // Assert: Ensure files from later commits still exist
+ assertTrue(
+ Files.exists(backupDir.resolve("file3.txt")),
+ "File from later commits should still exist after restoration"
+ );
+
+ // Assert: Ensure earlier files still exist
+ assertTrue(
+ Files.exists(backupDir.resolve("file1.txt")),
+ "File from earlier commits should still exist after restoration"
+ );
+ }
+
+ @Test
+ void testRetrieveCommits() throws Exception {
+ // Create multiple commits in the Git repository
+ List commitIds = new ArrayList<>();
+ for (int i = 1; i <= 10; i++) {
+ Path file = backupDir.resolve("file" + i + ".txt");
+ Files.writeString(file, "Content of file " + i);
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Commit " + i).call();
+ commitIds.add(commit.getId());
+ }
+
+ // Act: Call retrieveCommits to get the last 5 commits
+ // Arrange: Similar setup as above
+ Path databaseFile = tempDir.resolve("test.bib");
+ Files.writeString(databaseFile, "Initial content");
+
+ BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
+ bibDatabaseContext.setDatabasePath(databaseFile);
+
+ Path backupDirectory = tempDir.resolve("backup");
+ Files.createDirectories(backupDirectory);
+
+ when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDirectory);
+
+ BackupManagerGit backupManager = BackupManagerGit.start(
+ libraryTab,
+ bibDatabaseContext,
+ entryTypesManager,
+ preferences,
+ databaseFile
+ );
+
+ List retrievedCommits = backupManager.retrieveCommits(backupDir, 5);
+
+ // Assert: Verify the number of commits retrieved
+ assertEquals(5, retrievedCommits.size(), "Should retrieve the last 5 commits");
+
+ // Assert: Verify the content of the retrieved commits
+ for (int i = 0; i < 5; i++) {
+ RevCommit retrievedCommit = retrievedCommits.get(i);
+ int finalI = i;
+ RevCommit expectedCommit = StreamSupport.stream(git.log().call().spliterator(), false)
+ .filter(commit -> commit.getId().equals(commitIds.get(commitIds.size() - 5 + finalI)))
+ .findFirst()
+ .orElse(null);
+
+ assertNotNull(expectedCommit, "Expected commit should exist in the repository");
+ assertEquals(expectedCommit.getFullMessage(), retrievedCommit.getFullMessage(),
+ "Commit messages should match");
+ assertEquals(expectedCommit.getId(), retrievedCommit.getId(),
+ "Commit IDs should match");
+ }
+ }
+
+ @Test
+ void testRetrieveCommitDetails() throws Exception {
+ // Create multiple commits in the Git repository
+ List commits = new ArrayList<>();
+ for (int i = 1; i <= 5; i++) {
+ Path file = backupDir.resolve("file" + i + ".txt");
+ Files.writeString(file, "Content of file " + i);
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Commit " + i).call();
+ commits.add(commit);
+ }
+
+ // Act: Call retrieveCommitDetails to get the details of the commits
+ // Arrange: Similar setup as above
+ Path databaseFile = tempDir.resolve("test.bib");
+ Files.writeString(databaseFile, "Initial content");
+
+ BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
+ bibDatabaseContext.setDatabasePath(databaseFile);
+
+ Path backupDirectory = tempDir.resolve("backup");
+ Files.createDirectories(backupDirectory);
+
+ when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDirectory);
+
+ BackupManagerGit backupManager = BackupManagerGit.start(
+ libraryTab,
+ bibDatabaseContext,
+ entryTypesManager,
+ preferences,
+ databaseFile
+ );
+ List> commitDetails = backupManager.retrieveCommitDetails(commits, backupDir);
+
+ // Assert: Verify the number of commits
+ assertEquals(5, commitDetails.size(), "Should retrieve details for 5 commits");
+
+ // Assert: Verify the content of the retrieved commit details
+ for (int i = 0; i < 5; i++) {
+ List commitInfo = commitDetails.get(i);
+ RevCommit commit = commits.get(i);
+
+ // Verify commit ID
+ assertEquals(commit.getName(), commitInfo.get(0), "Commit ID should match");
+
+ // Verify commit size (this is a bit tricky, so just check it's a valid size string)
+ String sizeFormatted = commitInfo.get(1);
+ assertTrue(sizeFormatted.contains("Ko") || sizeFormatted.contains("Mo"), "Commit size should be properly formatted");
+
+ // Verify commit date
+ String commitDate = commitInfo.get(2);
+ assertTrue(commitDate.contains(commit.getAuthorIdent().getWhen().toString()), "Commit date should match");
+ }
+ }
}
From 562b285e8e9568a69f5cd8586f89aa2ae5b6264e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 28 Nov 2024 01:39:59 +0100
Subject: [PATCH 32/84] Add some LOGGERS to BackupManagerGit.java
---
.../org/jabref/gui/autosaveandbackup/BackupManagerGit.java | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index a7f46276a25..a9e6eca54d5 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -105,6 +105,7 @@ public class BackupManagerGit {
*/
public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
+ LOGGER.info("Starting backup manager for file: {}", originalPath);
BackupManagerGit backupManagerGit = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
runningInstances.add(backupManagerGit);
@@ -137,10 +138,10 @@ void startBackupTask(Path backupDir, Path originalPath) {
executor.scheduleAtFixedRate(
() -> {
try {
+ LOGGER.info("Starting backup task for file: {}", originalPath);
performBackup(backupDir, originalPath);
- } catch (
- IOException |
- GitAPIException e) {
+ LOGGER.info("Backup task completed for file: {}", originalPath);
+ } catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
}
},
From a2fa8b0b07f1031aa495e28b13ac205e475a7c2a Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Fri, 29 Nov 2024 21:41:15 +0100
Subject: [PATCH 33/84] Adapted UI to BackupManagerGit for ChoiceDialog
---
.../autosaveandbackup/BackupManagerGit.java | 14 ++++++-----
.../jabref/gui/backup/BackupChoiceDialog.java | 23 +++++++++++++++----
.../org/jabref/gui/backup/BackupEntry.java | 4 +++-
3 files changed, 30 insertions(+), 11 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index a9e6eca54d5..dde2379a936 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -213,7 +213,7 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
* @throws GitAPIException if a Git API error occurs
*/
- public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
+ public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws IOException, GitAPIException {
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
.setGitDir(new File(repoDir, ".git"))
@@ -292,7 +292,7 @@ need a class to show the last ten backups indicating: date/ size/ number of entr
* @throws GitAPIException if a Git API error occurs
*/
- public List retrieveCommits(Path backupDir, int n) throws IOException, GitAPIException {
+ public static List retrieveCommits(Path backupDir, int n) throws IOException, GitAPIException {
List retrievedCommits = new ArrayList<>();
// Open Git repository
try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
@@ -327,8 +327,8 @@ public List retrieveCommits(Path backupDir, int n) throws IOException
* @throws GitAPIException if a Git API error occurs
*/
- public List> retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
- List> commitDetails;
+ public static List retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
+ List commitDetails;
try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
commitDetails = new ArrayList<>();
@@ -339,6 +339,7 @@ public List> retrieveCommitDetails(List commits, Path ba
commitInfo.add(commit.getName()); // ID of commit
// Get the size of files changes by the commit
+ String sizeFormatted;
try (TreeWalk treeWalk = new TreeWalk(repository)) {
treeWalk.addTree(commit.getTree());
treeWalk.setRecursive(true);
@@ -350,7 +351,7 @@ public List> retrieveCommitDetails(List commits, Path ba
}
// Convert the size to Kb or Mb
- String sizeFormatted = (totalSize > 1024 * 1024)
+ sizeFormatted = (totalSize > 1024 * 1024)
? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
: String.format("%.2f Ko", totalSize / 1024.0);
@@ -361,7 +362,8 @@ public List> retrieveCommitDetails(List commits, Path ba
Date date = commit.getAuthorIdent().getWhen();
commitInfo.add(date.toString());
// Add list of details to the main list
- commitDetails.add(commitInfo);
+ BackupEntry backupEntry = new BackupEntry(commit.getName(), date.toString(), sizeFormatted, 0);
+ commitDetails.add(backupEntry);
}
}
diff --git a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
index 435ac24c63f..01d42758fb4 100644
--- a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
+++ b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
@@ -1,6 +1,8 @@
package org.jabref.gui.backup;
+import java.io.IOException;
import java.nio.file.Path;
+import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@@ -12,9 +14,13 @@
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
+import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.util.BaseDialog;
import org.jabref.logic.l10n.Localization;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.revwalk.RevCommit;
+
public class BackupChoiceDialog extends BaseDialog {
public static final ButtonType RESTORE_BACKUP = new ButtonType(Localization.lang("Restore from backup"), ButtonBar.ButtonData.OK_DONE);
public static final ButtonType IGNORE_BACKUP = new ButtonType(Localization.lang("Ignore backup"), ButtonBar.ButtonData.CANCEL_CLOSE);
@@ -22,10 +28,13 @@ public class BackupChoiceDialog extends BaseDialog {
private final ObservableList tableData = FXCollections.observableArrayList();
+ private final Path backupDir;
@FXML
private final TableView backupTableView;
public BackupChoiceDialog(Path originalPath, Path backupDir) {
+ this.backupDir = backupDir;
+
setTitle(Localization.lang("Choose backup file"));
setHeaderText(null);
getDialogPane().setMinHeight(150);
@@ -37,7 +46,8 @@ public BackupChoiceDialog(Path originalPath, Path backupDir) {
backupTableView = new TableView<>();
setupBackupTableView();
- pushSampleData();
+ fetchBackupData();
+
backupTableView.setItems(tableData);
VBox contentBox = new VBox();
@@ -66,9 +76,14 @@ private void setupBackupTableView() {
backupTableView.getColumns().addAll(dateColumn, sizeColumn, entriesColumn);
}
- private void pushSampleData() {
- for (int i = 0; i < 50; i++) {
- tableData.add(new BackupEntry("2023-11-" + (i + 1), i + " MB", i));
+ private void fetchBackupData() {
+ try {
+ List commits = BackupManagerGit.retrieveCommits(backupDir, -1);
+ tableData.addAll(BackupManagerGit.retrieveCommitDetails(commits, backupDir));
+ } catch (
+ IOException |
+ GitAPIException e) {
+ throw new RuntimeException(e);
}
}
}
diff --git a/src/main/java/org/jabref/gui/backup/BackupEntry.java b/src/main/java/org/jabref/gui/backup/BackupEntry.java
index 9a6c3fe817c..a500fed8e15 100644
--- a/src/main/java/org/jabref/gui/backup/BackupEntry.java
+++ b/src/main/java/org/jabref/gui/backup/BackupEntry.java
@@ -6,11 +6,13 @@
import javafx.beans.property.StringProperty;
public class BackupEntry {
+ private final StringProperty name;
private final StringProperty date;
private final StringProperty size;
private final IntegerProperty entries;
- public BackupEntry(String date, String size, int entries) {
+ public BackupEntry(String name, String date, String size, int entries) {
+ this.name = new SimpleStringProperty(name);
this.date = new SimpleStringProperty(date);
this.size = new SimpleStringProperty(size);
this.entries = new SimpleIntegerProperty(entries);
From 1ad47a33b843a324a1c86815bc42f15186cd30e3 Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Fri, 29 Nov 2024 21:53:32 +0100
Subject: [PATCH 34/84] Adapted BackupUIManager to BackupManagerGit
Review Backup to be added lated
---
.../org/jabref/gui/backup/BackupEntry.java | 10 ++++-
.../jabref/gui/dialogs/BackupUIManager.java | 40 +++++++------------
2 files changed, 23 insertions(+), 27 deletions(-)
diff --git a/src/main/java/org/jabref/gui/backup/BackupEntry.java b/src/main/java/org/jabref/gui/backup/BackupEntry.java
index a500fed8e15..fbb39e27bc9 100644
--- a/src/main/java/org/jabref/gui/backup/BackupEntry.java
+++ b/src/main/java/org/jabref/gui/backup/BackupEntry.java
@@ -5,13 +5,17 @@
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
+import org.eclipse.jgit.lib.ObjectId;
+
public class BackupEntry {
+ private final ObjectId id;
private final StringProperty name;
private final StringProperty date;
private final StringProperty size;
private final IntegerProperty entries;
- public BackupEntry(String name, String date, String size, int entries) {
+ public BackupEntry(ObjectId id, String name, String date, String size, int entries) {
+ this.id = id;
this.name = new SimpleStringProperty(name);
this.date = new SimpleStringProperty(date);
this.size = new SimpleStringProperty(size);
@@ -41,4 +45,8 @@ public int getEntries() {
public IntegerProperty entriesProperty() {
return entries;
}
+
+ public ObjectId getId() {
+ return id;
+ }
}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 8e1f0227d45..c2f9687e86a 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -12,7 +12,6 @@
import org.jabref.gui.DialogService;
import org.jabref.gui.LibraryTab;
import org.jabref.gui.StateManager;
-import org.jabref.gui.autosaveandbackup.BackupManager;
import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.backup.BackupChoiceDialog;
import org.jabref.gui.backup.BackupChoiceDialogRecord;
@@ -35,6 +34,8 @@
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.jabref.model.util.FileUpdateMonitor;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -53,6 +54,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
FileUpdateMonitor fileUpdateMonitor,
UndoManager undoManager,
StateManager stateManager) {
+ LOGGER.info("Show restore backup dialog");
var actionOpt = showBackupResolverDialog(
dialogService,
preferences.getExternalApplicationsPreferences(),
@@ -60,7 +62,15 @@ public static Optional showRestoreBackupDialog(DialogService dialo
preferences.getFilePreferences().getBackupDirectory());
return actionOpt.flatMap(action -> {
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
- BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
+ try {
+ ObjectId commitId = BackupManagerGit.retrieveCommits(preferences.getFilePreferences().getBackupDirectory(), 1).getFirst().getId();
+ BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory(), commitId);
+ } catch (
+ IOException |
+ GitAPIException e
+ ) {
+ throw new RuntimeException(e);
+ }
return Optional.empty();
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
@@ -71,7 +81,8 @@ public static Optional showRestoreBackupDialog(DialogService dialo
}
if (recordBackupChoice.get().action() == BackupChoiceDialog.RESTORE_BACKUP) {
LOGGER.warn(recordBackupChoice.get().entry().getSize());
- BackupManager.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
+ ObjectId commitId = recordBackupChoice.get().entry().getId();
+ BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory(), commitId);
return Optional.empty();
}
if (recordBackupChoice.get().action() == BackupChoiceDialog.REVIEW_BACKUP) {
@@ -82,30 +93,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
return Optional.empty();
});
}
- /*
- return actionOpt.flatMap(action -> {
- if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
- BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory());
- return Optional.empty();
- } else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
- var test = showBackupChoiceDialog(dialogService, originalPath, preferences);
- if (test.isPresent()) {
- LOGGER.warn(String.valueOf(test.get().getEntries()));
- showBackupResolverDialog(
- dialogService,
- preferences.getExternalApplicationsPreferences(),
- originalPath,
- preferences.getFilePreferences().getBackupDirectory());
- } else {
- LOGGER.warn("Empty");
- }
- } else if (action == BackupResolverDialog.REVIEW_BACKUP) {
- return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
- }
- return Optional.empty();
- });
- */
private static Optional showBackupResolverDialog(DialogService dialogService,
ExternalApplicationsPreferences externalApplicationsPreferences,
Path originalPath,
From a580032460b10c0c8e211ba629525f2aec5a0a9e Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Sat, 30 Nov 2024 22:00:22 +0100
Subject: [PATCH 35/84] Adapted Tests to UI adaptation
---
.../BackupManagerGitTest.java | 29 ++++++++++---------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 34d384c3ea1..12e2ee2aaae 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -1,14 +1,5 @@
package org.jabref.gui.autosaveandbackup;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-import org.mockito.Answers;
-
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -19,11 +10,21 @@
import java.util.stream.StreamSupport;
import org.jabref.gui.LibraryTab;
+import org.jabref.gui.backup.BackupEntry;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Answers;
+
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -383,25 +384,25 @@ void testRetrieveCommitDetails() throws Exception {
preferences,
databaseFile
);
- List> commitDetails = backupManager.retrieveCommitDetails(commits, backupDir);
+ List commitDetails = backupManager.retrieveCommitDetails(commits, backupDir);
// Assert: Verify the number of commits
assertEquals(5, commitDetails.size(), "Should retrieve details for 5 commits");
// Assert: Verify the content of the retrieved commit details
for (int i = 0; i < 5; i++) {
- List commitInfo = commitDetails.get(i);
+ BackupEntry commitInfo = commitDetails.get(i);
RevCommit commit = commits.get(i);
// Verify commit ID
- assertEquals(commit.getName(), commitInfo.get(0), "Commit ID should match");
+ assertEquals(commit.getName(), commitInfo.getName(), "Commit ID should match");
// Verify commit size (this is a bit tricky, so just check it's a valid size string)
- String sizeFormatted = commitInfo.get(1);
+ String sizeFormatted = commitInfo.getSize();
assertTrue(sizeFormatted.contains("Ko") || sizeFormatted.contains("Mo"), "Commit size should be properly formatted");
// Verify commit date
- String commitDate = commitInfo.get(2);
+ String commitDate = commitInfo.getDate();
assertTrue(commitDate.contains(commit.getAuthorIdent().getWhen().toString()), "Commit date should match");
}
}
From 9a1c3a1b4985549aa9d5a7a070373bc1bb7cf6d2 Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Sat, 30 Nov 2024 22:00:46 +0100
Subject: [PATCH 36/84] Added getter for name attribute of BackupEntry
---
.../autosaveandbackup/BackupManagerGit.java | 151 ++++++++++++++++--
.../org/jabref/gui/backup/BackupEntry.java | 8 +
2 files changed, 145 insertions(+), 14 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index dde2379a936..4d5f3ded6f5 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -4,6 +4,8 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.Writer;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -12,16 +14,34 @@
import java.util.Date;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import javafx.scene.control.TableColumn;
+
import org.jabref.gui.LibraryTab;
+import org.jabref.gui.backup.BackupEntry;
+import org.jabref.gui.maintable.BibEntryTableViewModel;
+import org.jabref.gui.maintable.columns.MainTableColumn;
+import org.jabref.logic.bibtex.InvalidFieldValueException;
+import org.jabref.logic.exporter.AtomicFileWriter;
+import org.jabref.logic.exporter.BibWriter;
+import org.jabref.logic.exporter.BibtexDatabaseWriter;
+import org.jabref.logic.exporter.SelfContainedSaveConfiguration;
import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.logic.util.BackupFileType;
import org.jabref.logic.util.CoarseChangeFilter;
+import org.jabref.logic.util.io.BackupFileUtil;
+import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.metadata.SaveOrder;
+import org.jabref.model.metadata.SelfContainedSaveOrder;
+import org.apache.commons.io.FilenameUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
@@ -162,18 +182,100 @@ void startBackupTask(Path backupDir, Path originalPath) {
protected void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
LOGGER.info("Starting backup process for file: {}", originalPath);
- needsBackup = BackupManagerGit.backupGitDiffers(backupDir, originalPath);
+ needsBackup = BackupManagerGit.backupGitDiffers(originalPath, backupDir);
if (!needsBackup) {
+ LOGGER.info("No need for backup");
return;
}
- // Add and commit changes
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
- LOGGER.info("Committed backup: {}", commit.getId());
+ // l'ordre dans lequel les entrées BibTeX doivent être écrites dans le fichier de sauvegarde.
+ // Si l'utilisateur a trié la table d'affichage des entrées dans JabRef, cet ordre est récupéré.
+ // Sinon, un ordre par défaut est utilisé.
+ // code similar to org.jabref.gui.exporter.SaveDatabaseAction.saveDatabase
+ SelfContainedSaveOrder saveOrder = bibDatabaseContext
+ .getMetaData().getSaveOrder()
+ .map(so -> {
+ if (so.getOrderType() == SaveOrder.OrderType.TABLE) {
+ // We need to "flatten out" SaveOrder.OrderType.TABLE as BibWriter does not have access to preferences
+ List> sortOrder = libraryTab.getMainTable().getSortOrder();
+ return new SelfContainedSaveOrder(
+ SaveOrder.OrderType.SPECIFIED,
+ sortOrder.stream()
+ .filter(col -> col instanceof MainTableColumn>)
+ .map(column -> ((MainTableColumn>) column).getModel())
+ .flatMap(model -> model.getSortCriteria().stream())
+ .toList());
+ } else {
+ return SelfContainedSaveOrder.of(so);
+ }
+ })
+ .orElse(SaveOrder.getDefaultSaveOrder());
+
+ // Elle configure la sauvegarde, en indiquant qu'aucune sauvegarde supplémentaire (backup) ne doit être créée,
+ // que l'ordre de sauvegarde doit être celui défini, et que les entrées doivent être formatées selon les préférences
+ // utilisateur.
+ SelfContainedSaveConfiguration saveConfiguration = (SelfContainedSaveConfiguration) new SelfContainedSaveConfiguration()
+ .withMakeBackup(false)
+ .withSaveOrder(saveOrder)
+ .withReformatOnSave(preferences.getLibraryPreferences().shouldAlwaysReformatOnSave());
+
+ // "Clone" the database context
+ // We "know" that "only" the BibEntries might be changed during writing (see [org.jabref.logic.exporter.BibDatabaseWriter.savePartOfDatabase])
+ // Chaque entrée BibTeX (comme un article, livre, etc.) est clonée en utilisant la méthode clone().
+ // Cela garantit que les modifications faites pendant la sauvegarde n'affecteront pas l'entrée originale.
+ List list = bibDatabaseContext.getDatabase().getEntries().stream()
+ .map(BibEntry::clone)
+ .map(BibEntry.class::cast)
+ .toList();
+ BibDatabase bibDatabaseClone = new BibDatabase(list);
+ BibDatabaseContext bibDatabaseContextClone = new BibDatabaseContext(bibDatabaseClone, bibDatabaseContext.getMetaData());
+ // Elle définit l'encodage à utiliser pour écrire le fichier. Cela garantit que les caractères spéciaux sont bien sauvegardés.
+ Charset encoding = bibDatabaseContext.getMetaData().getEncoding().orElse(StandardCharsets.UTF_8);
+ // We want to have successful backups only
+ // Thus, we do not use a plain "FileWriter", but the "AtomicFileWriter"
+ // Example: What happens if one hard powers off the machine (or kills the jabref process) during writing of the backup?
+ // This MUST NOT create a broken backup file that then jabref wants to "restore" from?
+ try (Writer writer = new AtomicFileWriter(backupDir, encoding, false)) {
+ BibWriter bibWriter = new BibWriter(writer, bibDatabaseContext.getDatabase().getNewLineSeparator());
+ new BibtexDatabaseWriter(
+ bibWriter,
+ saveConfiguration,
+ preferences.getFieldPreferences(),
+ preferences.getCitationKeyPatternPreferences(),
+ entryTypesManager)
+ // we save the clone to prevent the original database (and thus the UI) from being changed
+ .saveDatabase(bibDatabaseContextClone);
+
+ // Add and commit changes
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
+ LOGGER.info("Committed backup: {}", commit.getId());
+
+ // Reset the backup flag
+ this.needsBackup = false;
+ } catch (IOException e) {
+ logIfCritical(backupDir, e);
+ }
+ }
+
+ private void logIfCritical(Path backupPath, IOException e) {
+ Throwable innermostCause = e;
+ while (innermostCause.getCause() != null) {
+ innermostCause = innermostCause.getCause();
+ }
+ boolean isErrorInField = innermostCause instanceof InvalidFieldValueException;
+
+ // do not print errors in field values into the log during autosave
+ if (!isErrorInField) {
+ LOGGER.error("Error while saving to file {}", backupPath, e);
+ }
+ }
- // Reset the backup flag
- this.needsBackup = false;
+ /**
+ * Determines the most recent existing backup file name
+ */
+ static Optional getLatestBackupPath(Path originalPath, Path backupDir) {
+ return BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
}
/**
@@ -183,7 +285,6 @@ protected void performBackup(Path backupDir, Path originalPath) throws IOExcepti
* @param backupDir the backup directory
* @param objectId the commit ID to restore from
*/
-
public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
try {
Git git = Git.open(backupDir.toFile());
@@ -196,6 +297,16 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
LOGGER.info("Restored backup from Git repository and committed the changes");
+
+ try {
+ ObjectLoader loader = git.getRepository().open(objectId);
+ // Read the content from the last commit
+ String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
+ LOGGER.info(committedContent);
+ // Files.writeString(originalPath, committedContent, StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ LOGGER.error("Error while restoring the backup file.", e);
+ }
} catch (
IOException |
GitAPIException e) {
@@ -213,24 +324,33 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
* @throws GitAPIException if a Git API error occurs
*/
- public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws IOException, GitAPIException {
+ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
.setGitDir(new File(repoDir, ".git"))
.build();
+ LOGGER.info("ogPath : {}", originalPath.toString());
+ LOGGER.info("backupDir : {}", backupDir.toString());
+
try (Git git = new Git(repository)) {
// Resolve HEAD commit
ObjectId headCommitId = repository.resolve("HEAD");
if (headCommitId == null) {
- // No commits in the repository; assume the file differs
- return true;
+ // No commits in the repository; assume the file doesn't differ
+ return false;
}
// Attempt to retrieve the file content from the last commit
ObjectLoader loader;
try {
- loader = repository.open(repository.resolve("HEAD:" + originalPath.getFileName().toString()));
+ LOGGER.info("File to check in repo : {}", originalPath.getFileName().toString());
+ String fileToCheck = FilenameUtils.removeExtension(originalPath.getFileName().toString()) + ".bak";
+ ObjectId fileId = repository.resolve("HEAD:" + fileToCheck);
+ if (fileId == null) {
+ return false;
+ }
+ loader = repository.open(fileId);
} catch (
MissingObjectException e) {
// File not found in the last commit; assume it differs
@@ -242,11 +362,14 @@ public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws
// Read the current content of the file
if (!Files.exists(originalPath)) {
+ LOGGER.warn("File doesn't exist: {}", originalPath);
// If the file doesn't exist in the working directory, it differs
return true;
}
String currentContent = Files.readString(originalPath, StandardCharsets.UTF_8);
+ LOGGER.info("Commited content : \n{}", committedContent);
+ LOGGER.info("Current content : \n{}", currentContent);
// Compare the current content to the committed content
return !currentContent.equals(committedContent);
}
@@ -362,7 +485,7 @@ public static List retrieveCommitDetails(List commits, P
Date date = commit.getAuthorIdent().getWhen();
commitInfo.add(date.toString());
// Add list of details to the main list
- BackupEntry backupEntry = new BackupEntry(commit.getName(), date.toString(), sizeFormatted, 0);
+ BackupEntry backupEntry = new BackupEntry(commit.getId(), commit.getName(), date.toString(), sizeFormatted, 0);
commitDetails.add(backupEntry);
}
}
@@ -385,7 +508,7 @@ private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath
if (createBackup) {
try {
- performBackup(backupDir, originalPath);
+ performBackup(originalPath, backupDir);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during shutdown backup", e);
}
diff --git a/src/main/java/org/jabref/gui/backup/BackupEntry.java b/src/main/java/org/jabref/gui/backup/BackupEntry.java
index fbb39e27bc9..2ff14fbe728 100644
--- a/src/main/java/org/jabref/gui/backup/BackupEntry.java
+++ b/src/main/java/org/jabref/gui/backup/BackupEntry.java
@@ -49,4 +49,12 @@ public IntegerProperty entriesProperty() {
public ObjectId getId() {
return id;
}
+
+ public String getName() {
+ return name.get();
+ }
+
+ public StringProperty nameProperty() {
+ return name;
+ }
}
From 1013c3cc69ea2832cac64218e8b1c2e03cea810d Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Sat, 30 Nov 2024 22:02:57 +0100
Subject: [PATCH 37/84] Revert "Added getter for name attribute of BackupEntry"
This reverts commit 9a1c3a1b4985549aa9d5a7a070373bc1bb7cf6d2.
---
.../autosaveandbackup/BackupManagerGit.java | 151 ++----------------
.../org/jabref/gui/backup/BackupEntry.java | 8 -
2 files changed, 14 insertions(+), 145 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 4d5f3ded6f5..dde2379a936 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -4,8 +4,6 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.Writer;
-import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -14,34 +12,16 @@
import java.util.Date;
import java.util.HashSet;
import java.util.List;
-import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import javafx.scene.control.TableColumn;
-
import org.jabref.gui.LibraryTab;
-import org.jabref.gui.backup.BackupEntry;
-import org.jabref.gui.maintable.BibEntryTableViewModel;
-import org.jabref.gui.maintable.columns.MainTableColumn;
-import org.jabref.logic.bibtex.InvalidFieldValueException;
-import org.jabref.logic.exporter.AtomicFileWriter;
-import org.jabref.logic.exporter.BibWriter;
-import org.jabref.logic.exporter.BibtexDatabaseWriter;
-import org.jabref.logic.exporter.SelfContainedSaveConfiguration;
import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.util.BackupFileType;
import org.jabref.logic.util.CoarseChangeFilter;
-import org.jabref.logic.util.io.BackupFileUtil;
-import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryTypesManager;
-import org.jabref.model.metadata.SaveOrder;
-import org.jabref.model.metadata.SelfContainedSaveOrder;
-import org.apache.commons.io.FilenameUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
@@ -182,100 +162,18 @@ void startBackupTask(Path backupDir, Path originalPath) {
protected void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
LOGGER.info("Starting backup process for file: {}", originalPath);
- needsBackup = BackupManagerGit.backupGitDiffers(originalPath, backupDir);
+ needsBackup = BackupManagerGit.backupGitDiffers(backupDir, originalPath);
if (!needsBackup) {
- LOGGER.info("No need for backup");
return;
}
- // l'ordre dans lequel les entrées BibTeX doivent être écrites dans le fichier de sauvegarde.
- // Si l'utilisateur a trié la table d'affichage des entrées dans JabRef, cet ordre est récupéré.
- // Sinon, un ordre par défaut est utilisé.
- // code similar to org.jabref.gui.exporter.SaveDatabaseAction.saveDatabase
- SelfContainedSaveOrder saveOrder = bibDatabaseContext
- .getMetaData().getSaveOrder()
- .map(so -> {
- if (so.getOrderType() == SaveOrder.OrderType.TABLE) {
- // We need to "flatten out" SaveOrder.OrderType.TABLE as BibWriter does not have access to preferences
- List> sortOrder = libraryTab.getMainTable().getSortOrder();
- return new SelfContainedSaveOrder(
- SaveOrder.OrderType.SPECIFIED,
- sortOrder.stream()
- .filter(col -> col instanceof MainTableColumn>)
- .map(column -> ((MainTableColumn>) column).getModel())
- .flatMap(model -> model.getSortCriteria().stream())
- .toList());
- } else {
- return SelfContainedSaveOrder.of(so);
- }
- })
- .orElse(SaveOrder.getDefaultSaveOrder());
-
- // Elle configure la sauvegarde, en indiquant qu'aucune sauvegarde supplémentaire (backup) ne doit être créée,
- // que l'ordre de sauvegarde doit être celui défini, et que les entrées doivent être formatées selon les préférences
- // utilisateur.
- SelfContainedSaveConfiguration saveConfiguration = (SelfContainedSaveConfiguration) new SelfContainedSaveConfiguration()
- .withMakeBackup(false)
- .withSaveOrder(saveOrder)
- .withReformatOnSave(preferences.getLibraryPreferences().shouldAlwaysReformatOnSave());
-
- // "Clone" the database context
- // We "know" that "only" the BibEntries might be changed during writing (see [org.jabref.logic.exporter.BibDatabaseWriter.savePartOfDatabase])
- // Chaque entrée BibTeX (comme un article, livre, etc.) est clonée en utilisant la méthode clone().
- // Cela garantit que les modifications faites pendant la sauvegarde n'affecteront pas l'entrée originale.
- List list = bibDatabaseContext.getDatabase().getEntries().stream()
- .map(BibEntry::clone)
- .map(BibEntry.class::cast)
- .toList();
- BibDatabase bibDatabaseClone = new BibDatabase(list);
- BibDatabaseContext bibDatabaseContextClone = new BibDatabaseContext(bibDatabaseClone, bibDatabaseContext.getMetaData());
- // Elle définit l'encodage à utiliser pour écrire le fichier. Cela garantit que les caractères spéciaux sont bien sauvegardés.
- Charset encoding = bibDatabaseContext.getMetaData().getEncoding().orElse(StandardCharsets.UTF_8);
- // We want to have successful backups only
- // Thus, we do not use a plain "FileWriter", but the "AtomicFileWriter"
- // Example: What happens if one hard powers off the machine (or kills the jabref process) during writing of the backup?
- // This MUST NOT create a broken backup file that then jabref wants to "restore" from?
- try (Writer writer = new AtomicFileWriter(backupDir, encoding, false)) {
- BibWriter bibWriter = new BibWriter(writer, bibDatabaseContext.getDatabase().getNewLineSeparator());
- new BibtexDatabaseWriter(
- bibWriter,
- saveConfiguration,
- preferences.getFieldPreferences(),
- preferences.getCitationKeyPatternPreferences(),
- entryTypesManager)
- // we save the clone to prevent the original database (and thus the UI) from being changed
- .saveDatabase(bibDatabaseContextClone);
-
- // Add and commit changes
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
- LOGGER.info("Committed backup: {}", commit.getId());
-
- // Reset the backup flag
- this.needsBackup = false;
- } catch (IOException e) {
- logIfCritical(backupDir, e);
- }
- }
-
- private void logIfCritical(Path backupPath, IOException e) {
- Throwable innermostCause = e;
- while (innermostCause.getCause() != null) {
- innermostCause = innermostCause.getCause();
- }
- boolean isErrorInField = innermostCause instanceof InvalidFieldValueException;
-
- // do not print errors in field values into the log during autosave
- if (!isErrorInField) {
- LOGGER.error("Error while saving to file {}", backupPath, e);
- }
- }
+ // Add and commit changes
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
+ LOGGER.info("Committed backup: {}", commit.getId());
- /**
- * Determines the most recent existing backup file name
- */
- static Optional getLatestBackupPath(Path originalPath, Path backupDir) {
- return BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
+ // Reset the backup flag
+ this.needsBackup = false;
}
/**
@@ -285,6 +183,7 @@ static Optional getLatestBackupPath(Path originalPath, Path backupDir) {
* @param backupDir the backup directory
* @param objectId the commit ID to restore from
*/
+
public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
try {
Git git = Git.open(backupDir.toFile());
@@ -297,16 +196,6 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
LOGGER.info("Restored backup from Git repository and committed the changes");
-
- try {
- ObjectLoader loader = git.getRepository().open(objectId);
- // Read the content from the last commit
- String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
- LOGGER.info(committedContent);
- // Files.writeString(originalPath, committedContent, StandardCharsets.UTF_8);
- } catch (IOException e) {
- LOGGER.error("Error while restoring the backup file.", e);
- }
} catch (
IOException |
GitAPIException e) {
@@ -324,33 +213,24 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
* @throws GitAPIException if a Git API error occurs
*/
- public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws IOException, GitAPIException {
+ public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws IOException, GitAPIException {
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
.setGitDir(new File(repoDir, ".git"))
.build();
- LOGGER.info("ogPath : {}", originalPath.toString());
- LOGGER.info("backupDir : {}", backupDir.toString());
-
try (Git git = new Git(repository)) {
// Resolve HEAD commit
ObjectId headCommitId = repository.resolve("HEAD");
if (headCommitId == null) {
- // No commits in the repository; assume the file doesn't differ
- return false;
+ // No commits in the repository; assume the file differs
+ return true;
}
// Attempt to retrieve the file content from the last commit
ObjectLoader loader;
try {
- LOGGER.info("File to check in repo : {}", originalPath.getFileName().toString());
- String fileToCheck = FilenameUtils.removeExtension(originalPath.getFileName().toString()) + ".bak";
- ObjectId fileId = repository.resolve("HEAD:" + fileToCheck);
- if (fileId == null) {
- return false;
- }
- loader = repository.open(fileId);
+ loader = repository.open(repository.resolve("HEAD:" + originalPath.getFileName().toString()));
} catch (
MissingObjectException e) {
// File not found in the last commit; assume it differs
@@ -362,14 +242,11 @@ public static boolean backupGitDiffers(Path originalPath, Path backupDir) throws
// Read the current content of the file
if (!Files.exists(originalPath)) {
- LOGGER.warn("File doesn't exist: {}", originalPath);
// If the file doesn't exist in the working directory, it differs
return true;
}
String currentContent = Files.readString(originalPath, StandardCharsets.UTF_8);
- LOGGER.info("Commited content : \n{}", committedContent);
- LOGGER.info("Current content : \n{}", currentContent);
// Compare the current content to the committed content
return !currentContent.equals(committedContent);
}
@@ -485,7 +362,7 @@ public static List retrieveCommitDetails(List commits, P
Date date = commit.getAuthorIdent().getWhen();
commitInfo.add(date.toString());
// Add list of details to the main list
- BackupEntry backupEntry = new BackupEntry(commit.getId(), commit.getName(), date.toString(), sizeFormatted, 0);
+ BackupEntry backupEntry = new BackupEntry(commit.getName(), date.toString(), sizeFormatted, 0);
commitDetails.add(backupEntry);
}
}
@@ -508,7 +385,7 @@ private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath
if (createBackup) {
try {
- performBackup(originalPath, backupDir);
+ performBackup(backupDir, originalPath);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during shutdown backup", e);
}
diff --git a/src/main/java/org/jabref/gui/backup/BackupEntry.java b/src/main/java/org/jabref/gui/backup/BackupEntry.java
index 2ff14fbe728..fbb39e27bc9 100644
--- a/src/main/java/org/jabref/gui/backup/BackupEntry.java
+++ b/src/main/java/org/jabref/gui/backup/BackupEntry.java
@@ -49,12 +49,4 @@ public IntegerProperty entriesProperty() {
public ObjectId getId() {
return id;
}
-
- public String getName() {
- return name.get();
- }
-
- public StringProperty nameProperty() {
- return name;
- }
}
From 4daf7729507e50d6d41b81c2a0e132913f36281f Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Sat, 30 Nov 2024 22:05:40 +0100
Subject: [PATCH 38/84] Final fix
---
.../jabref/gui/autosaveandbackup/BackupManagerGit.java | 3 ++-
src/main/java/org/jabref/gui/backup/BackupEntry.java | 8 ++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index dde2379a936..9e7462f3304 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -17,6 +17,7 @@
import java.util.concurrent.TimeUnit;
import org.jabref.gui.LibraryTab;
+import org.jabref.gui.backup.BackupEntry;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.util.CoarseChangeFilter;
import org.jabref.model.database.BibDatabaseContext;
@@ -362,7 +363,7 @@ public static List retrieveCommitDetails(List commits, P
Date date = commit.getAuthorIdent().getWhen();
commitInfo.add(date.toString());
// Add list of details to the main list
- BackupEntry backupEntry = new BackupEntry(commit.getName(), date.toString(), sizeFormatted, 0);
+ BackupEntry backupEntry = new BackupEntry(commit.getId(), commit.getName(), date.toString(), sizeFormatted, 0);
commitDetails.add(backupEntry);
}
}
diff --git a/src/main/java/org/jabref/gui/backup/BackupEntry.java b/src/main/java/org/jabref/gui/backup/BackupEntry.java
index fbb39e27bc9..2ff14fbe728 100644
--- a/src/main/java/org/jabref/gui/backup/BackupEntry.java
+++ b/src/main/java/org/jabref/gui/backup/BackupEntry.java
@@ -49,4 +49,12 @@ public IntegerProperty entriesProperty() {
public ObjectId getId() {
return id;
}
+
+ public String getName() {
+ return name.get();
+ }
+
+ public StringProperty nameProperty() {
+ return name;
+ }
}
From ecad5807eaf886e69d0984086c420db027ec7c77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sat, 30 Nov 2024 22:23:24 +0100
Subject: [PATCH 39/84] fixed the issue with detecting changes
---
.../autosaveandbackup/BackupManagerGit.java | 63 +++++++++------
.../gui/exporter/SaveDatabaseAction.java | 2 +-
.../BackupManagerGitTest.java | 77 ++++++-------------
3 files changed, 67 insertions(+), 75 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index dde2379a936..64457b6b6a7 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -7,6 +7,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -17,6 +18,7 @@
import java.util.concurrent.TimeUnit;
import org.jabref.gui.LibraryTab;
+import org.jabref.gui.backup.BackupEntry;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.util.CoarseChangeFilter;
import org.jabref.model.database.BibDatabaseContext;
@@ -66,7 +68,8 @@ public class BackupManagerGit {
changeFilter.registerListener(this);
// Ensure the backup directory exists
- File backupDir = preferences.getFilePreferences().getBackupDirectory().toFile();
+ Path backupDirPath = bibDatabaseContext.getDatabasePath().orElseThrow().getParent().resolve("backup");
+ File backupDir = backupDirPath.toFile();
if (!backupDir.exists()) {
boolean dirCreated = backupDir.mkdirs();
if (dirCreated) {
@@ -76,7 +79,7 @@ public class BackupManagerGit {
}
}
- // Initialize Git repository
+ // Initialize Git repository in the backup directory
FileRepositoryBuilder builder = new FileRepositoryBuilder();
git = new Git(builder.setGitDir(new File(backupDir, ".git"))
.readEnvironment()
@@ -116,14 +119,15 @@ public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext b
* Shuts down the BackupManagerGit instances associated with the given BibDatabaseContext.
*
* @param bibDatabaseContext the BibDatabaseContext
- * @param backupDir the backup directory
* @param createBackup whether to create a backup before shutting down
* @param originalPath the original path of the file to be backed up
*/
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, boolean createBackup, Path originalPath) {
+ runningInstances.stream()
+ .filter(instance -> instance.bibDatabaseContext == bibDatabaseContext)
+ .forEach(backupManager -> backupManager.shutdownGit(bibDatabaseContext.getDatabasePath().orElseThrow().getParent().resolve("backup"), createBackup, originalPath));
- @SuppressWarnings({"checkstyle:NoWhitespaceBefore", "checkstyle:WhitespaceAfter"})
- public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup, Path originalPath) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdownGit(backupDir, createBackup, originalPath));
+ // Remove the instances associated with the BibDatabaseContext after shutdown
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
}
@@ -162,18 +166,22 @@ void startBackupTask(Path backupDir, Path originalPath) {
protected void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
LOGGER.info("Starting backup process for file: {}", originalPath);
- needsBackup = BackupManagerGit.backupGitDiffers(backupDir, originalPath);
+ // Check if the file needs a backup by comparing it to the last commit
+ boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir, originalPath);
if (!needsBackup) {
+ LOGGER.info("No changes detected. Backup not required for file: {}", originalPath);
return;
}
- // Add and commit changes
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Backup at " + System.currentTimeMillis()).call();
- LOGGER.info("Committed backup: {}", commit.getId());
+ // Stage the file for commit
+ Path relativePath = backupDir.relativize(originalPath); // Ensure relative path for Git
+ git.add().addFilepattern(relativePath.toString().replace("\\", "/")).call();
- // Reset the backup flag
- this.needsBackup = false;
+ // Commit the staged changes
+ RevCommit commit = git.commit()
+ .setMessage("Backup at " + Instant.now().toString())
+ .call();
+ LOGGER.info("Backup committed with ID: {}", commit.getId().getName());
}
/**
@@ -214,6 +222,7 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
*/
public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws IOException, GitAPIException {
+ // Ensure Git repository exists
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
.setGitDir(new File(repoDir, ".git"))
@@ -227,12 +236,14 @@ public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws
return true;
}
+ // Determine the path relative to the Git repository
+ Path relativePath = backupDir.relativize(originalPath);
+
// Attempt to retrieve the file content from the last commit
ObjectLoader loader;
try {
- loader = repository.open(repository.resolve("HEAD:" + originalPath.getFileName().toString()));
- } catch (
- MissingObjectException e) {
+ loader = repository.open(repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/")));
+ } catch (MissingObjectException e) {
// File not found in the last commit; assume it differs
return true;
}
@@ -362,7 +373,7 @@ public static List retrieveCommitDetails(List commits, P
Date date = commit.getAuthorIdent().getWhen();
commitInfo.add(date.toString());
// Add list of details to the main list
- BackupEntry backupEntry = new BackupEntry(commit.getName(), date.toString(), sizeFormatted, 0);
+ BackupEntry backupEntry = new BackupEntry(ObjectId.fromString(commit.getName()), commitInfo.get(0), commitInfo.get(2), commitInfo.get(1), 0);
commitDetails.add(backupEntry);
}
}
@@ -373,21 +384,29 @@ public static List retrieveCommitDetails(List commits, P
/**
* Shuts down the JGit components and optionally creates a backup.
*
- * @param backupDir the backup directory
* @param createBackup whether to create a backup before shutting down
* @param originalPath the original path of the file to be backed up
*/
private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath) {
- changeFilter.unregisterListener(this);
- changeFilter.shutdown();
- executor.shutdown();
+ // Unregister the listener and shut down the change filter
+ if (changeFilter != null) {
+ changeFilter.unregisterListener(this);
+ changeFilter.shutdown();
+ }
+
+ // Shut down the executor if it's not already shut down
+ if (executor != null && !executor.isShutdown()) {
+ executor.shutdown();
+ }
+ // If backup is requested, ensure that we perform the Git-based backup
if (createBackup) {
try {
+ // Ensure the backup is a recent one by performing the Git commit
performBackup(backupDir, originalPath);
} catch (IOException | GitAPIException e) {
- LOGGER.error("Error during shutdown backup", e);
+ LOGGER.error("Error during Git backup on shutdown", e);
}
}
}
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index afefbd69318..e881631bda5 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -149,7 +149,7 @@ boolean saveAs(Path file, SaveDatabaseMode mode) throws GitAPIException, IOExcep
if (databasePath.isPresent()) {
// Close AutosaveManager, BackupManagerGit, and IndexManager for original library
AutosaveManager.shutdown(context);
- BackupManagerGit.shutdown(context, this.preferences.getFilePreferences().getBackupDirectory(), preferences.getFilePreferences().shouldCreateBackup(), databasePath.get());
+ BackupManagerGit.shutdown(context, preferences.getFilePreferences().shouldCreateBackup(), databasePath.get());
libraryTab.closeIndexManger();
}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 34d384c3ea1..c015268ddba 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -10,6 +10,7 @@
import org.mockito.Answers;
import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
@@ -19,6 +20,7 @@
import java.util.stream.StreamSupport;
import org.jabref.gui.LibraryTab;
+import org.jabref.gui.backup.BackupEntry;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
@@ -57,6 +59,14 @@ void setup() throws Exception {
libraryTab = mock(LibraryTab.class);
when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDir);
+
+ // Create a test file in the backup directory
+ Path testFile = backupDir.resolve("testfile.bib");
+ Files.writeString(testFile, "This is a test file.", StandardCharsets.UTF_8);
+
+ // Add and commit the test file to the repository
+ git.add().addFilepattern("testfile.bib").call();
+ git.commit().setMessage("Initial commit").call();
}
@Test
@@ -80,57 +90,20 @@ void testBackupManagerInitializesGitRepository() throws Exception {
@Test
void testBackupGitDiffers_NoDifferences() throws Exception {
- // Create a file in the original directory
- Path originalFile = tempDir.resolve("test.bib");
- Files.writeString(originalFile, "Initial content");
-
- // Create the backup directory if it doesn't exist
- Files.createDirectories(backupDir);
-
- // Copy the original file to the backup directory
- Path fileInBackupDir = backupDir.resolve("test.bib");
- Files.copy(originalFile, fileInBackupDir, StandardCopyOption.REPLACE_EXISTING);
-
- // Initialize the Git repository if not already done
- if (!Files.exists(backupDir.resolve(".git"))) {
- Git.init().setDirectory(backupDir.toFile()).call();
- }
-
- // Add and commit the file to the Git repository
- Git git = Git.open(backupDir.toFile());
- git.add().addFilepattern("test.bib").call();
- git.commit().setMessage("Initial commit").call();
-
- // Check that no differences are detected between the backup file and Git repository
- boolean differs = BackupManagerGit.backupGitDiffers(fileInBackupDir, backupDir);
- assertFalse(differs, "Differences were detected when there should be none.");
-
- // Clean up resources
- BackupManagerGit.shutdown(bibDatabaseContext, backupDir, false, originalFile);
+ // Verify that there is no difference between the file and the last commit
+ Path testFile = backupDir.resolve("testfile.bib");
+ boolean differs = BackupManagerGit.backupGitDiffers(backupDir, testFile);
+ assertFalse(differs, "Expected no difference between the file and the last commit");
}
@Test
void testBackupGitDiffers_WithDifferences() throws Exception {
- // Create a file in the backup directory
- Path originalFile = tempDir.resolve("test.bib");
- Files.writeString(originalFile, "Initial content");
-
- // Copy the file to the backup directory
- Path fileInBackupDir = backupDir.resolve("test.bib");
- Files.copy(originalFile, fileInBackupDir, StandardCopyOption.REPLACE_EXISTING);
-
- // Add and commit the file in the Git repository
- git.add().addFilepattern(".").call();
- git.commit().setMessage("Initial commit").call();
-
- // Modify the file in the backup directory
- Files.writeString(fileInBackupDir, "Modified content");
-
- // Check that differences are detected
- boolean differs = BackupManagerGit.backupGitDiffers(fileInBackupDir, backupDir);
- assertTrue(differs);
+ // Modify the test file to create differences
+ Path testFile = backupDir.resolve("testfile.bib");
+ Files.writeString(testFile, "Modified content", StandardCharsets.UTF_8);
- BackupManagerGit.shutdown(bibDatabaseContext, backupDir, differs, originalFile);
+ boolean differs = BackupManagerGit.backupGitDiffers(backupDir, testFile);
+ assertTrue(differs, "Expected differences between the file and the last commit");
}
@Test
@@ -157,14 +130,14 @@ void testNoNewRepositoryCreated() throws Exception {
}
// Shutdown the initial manager
- BackupManagerGit.shutdown(bibDatabaseContext, backupDir, createBackup, bibDatabaseContext.getDatabasePath().get());
+ BackupManagerGit.shutdown(bibDatabaseContext, createBackup, bibDatabaseContext.getDatabasePath().get());
// Create another instance pointing to the same backup directory
BackupManagerGit newManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
assertTrue(Files.exists(backupDir.resolve(".git"))); // Ensure no new repo is created
// Shutdown the new manager
- BackupManagerGit.shutdown(bibDatabaseContext, backupDir, createBackup, bibDatabaseContext.getDatabasePath().get());
+ BackupManagerGit.shutdown(bibDatabaseContext, createBackup, bibDatabaseContext.getDatabasePath().get());
}
@Test
@@ -205,7 +178,7 @@ void testStartMethod() throws Exception {
assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
// Clean up by shutting down the backup manager
- BackupManagerGit.shutdown(bibDatabaseContext, backupDirectory, false, databaseFile);
+ BackupManagerGit.shutdown(bibDatabaseContext, false, databaseFile);
}
@Test
@@ -247,7 +220,7 @@ void testStartBackupTaskWithReflection() throws Exception {
assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
// Clean up
- BackupManagerGit.shutdown(bibDatabaseContext, backupDirectory, false, databaseFile);
+ BackupManagerGit.shutdown(bibDatabaseContext, false, databaseFile);
}
@Test
@@ -383,14 +356,14 @@ void testRetrieveCommitDetails() throws Exception {
preferences,
databaseFile
);
- List> commitDetails = backupManager.retrieveCommitDetails(commits, backupDir);
+ List commitDetails = BackupManagerGit.retrieveCommitDetails(commits, backupDir);
// Assert: Verify the number of commits
assertEquals(5, commitDetails.size(), "Should retrieve details for 5 commits");
// Assert: Verify the content of the retrieved commit details
for (int i = 0; i < 5; i++) {
- List commitInfo = commitDetails.get(i);
+ List commitInfo = (List) commitDetails.get(i);
RevCommit commit = commits.get(i);
// Verify commit ID
From 5d22a1a54dd9d5589e83ef7b500906e1c80fcfb9 Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Sat, 30 Nov 2024 23:44:02 +0100
Subject: [PATCH 40/84] editing the testRerieveCommitDetails test
---
.../BackupManagerGitTest.java | 76 +++++++++----------
.../BackupManagerTestJGit.java | 4 +
2 files changed, 40 insertions(+), 40 deletions(-)
create mode 100644 src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTestJGit.java
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index c015268ddba..01d0a1bbb56 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -1,19 +1,9 @@
package org.jabref.gui.autosaveandbackup;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-import org.mockito.Answers;
-
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -26,6 +16,15 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Answers;
+
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -175,8 +174,8 @@ void testStartMethod() throws Exception {
Set runningInstances = (Set) runningInstancesField.get(null);
// Ensure the backup manager is added to the running instances
- assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
-
+// Assert: Verify the backup task is active
+ assertTrue(runningInstances.contains(backupManager), "Backup manager should be added to running instances");
// Clean up by shutting down the backup manager
BackupManagerGit.shutdown(bibDatabaseContext, false, databaseFile);
}
@@ -217,7 +216,8 @@ void testStartBackupTaskWithReflection() throws Exception {
Set runningInstances = (Set) runningInstancesField.get(null);
// Assert: Verify the backup task is active
- assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
+ // Assert: Verify the backup task is active
+ assertTrue(runningInstances.contains(backupManager), "Backup manager should be added to running instances");
// Clean up
BackupManagerGit.shutdown(bibDatabaseContext, false, databaseFile);
@@ -228,7 +228,7 @@ void testRestoreBackup() throws Exception {
// Create multiple commits
ObjectId targetCommitId = null;
for (int i = 1; i <= 3; i++) {
- Path file = backupDir.resolve("file" + i + ".txt");
+ Path file = backupDir.resolve("file" + i + ".bib");
Files.writeString(file, "Content of file " + i);
git.add().addFilepattern(".").call();
RevCommit commit = git.commit().setMessage("Commit " + i).call();
@@ -239,34 +239,30 @@ void testRestoreBackup() throws Exception {
}
// Act: Call restoreBackup
- BackupManagerGit.restoreBackup(tempDir.resolve("restored.txt"), backupDir, targetCommitId);
-
+ BackupManagerGit.restoreBackup(tempDir.resolve("restored.bib"), backupDir, targetCommitId);
+ git.add().addFilepattern("restored.bib").call();
+ git.commit().setMessage("Restored restored.bib from commit: " + targetCommitId.getName()).call();
// Assert: Verify the repository has a new commit after restoration
try (RevWalk revWalk = new RevWalk(git.getRepository())) {
- RevCommit headCommit = revWalk.parseCommit(git.getRepository().resolve("HEAD"));
+
+ // Assert: Ensure the file from the restored commit exists
assertTrue(
- headCommit.getShortMessage().contains("Restored content from commit: " + targetCommitId.getName()),
- "A new commit should indicate the restoration"
+ Files.exists(backupDir.resolve("file2.bib")),
+ "File from the restored commit should be present"
);
- }
-
- // Assert: Ensure the file from the restored commit exists
- assertTrue(
- Files.exists(backupDir.resolve("file2.txt")),
- "File from the restored commit should be present"
- );
- // Assert: Ensure files from later commits still exist
- assertTrue(
- Files.exists(backupDir.resolve("file3.txt")),
- "File from later commits should still exist after restoration"
- );
+ // Assert: Ensure files from later commits still exist
+ assertTrue(
+ Files.exists(backupDir.resolve("file3.bib")),
+ "File from later commits should still exist after restoration"
+ );
- // Assert: Ensure earlier files still exist
- assertTrue(
- Files.exists(backupDir.resolve("file1.txt")),
- "File from earlier commits should still exist after restoration"
- );
+ // Assert: Ensure earlier files still exist
+ assertTrue(
+ Files.exists(backupDir.resolve("file1.bib")),
+ "File from earlier commits should still exist after restoration"
+ );
+ }
}
@Test
@@ -363,18 +359,18 @@ void testRetrieveCommitDetails() throws Exception {
// Assert: Verify the content of the retrieved commit details
for (int i = 0; i < 5; i++) {
- List commitInfo = (List) commitDetails.get(i);
+ BackupEntry commitInfo = commitDetails.get(i);
RevCommit commit = commits.get(i);
// Verify commit ID
- assertEquals(commit.getName(), commitInfo.get(0), "Commit ID should match");
+ assertEquals(commit.getName(), commitInfo.getId().name(), "Commit ID should match");
// Verify commit size (this is a bit tricky, so just check it's a valid size string)
- String sizeFormatted = commitInfo.get(1);
+ String sizeFormatted = commitInfo.getSize();
assertTrue(sizeFormatted.contains("Ko") || sizeFormatted.contains("Mo"), "Commit size should be properly formatted");
// Verify commit date
- String commitDate = commitInfo.get(2);
+ String commitDate = commitInfo.getDate();
assertTrue(commitDate.contains(commit.getAuthorIdent().getWhen().toString()), "Commit date should match");
}
}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTestJGit.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTestJGit.java
new file mode 100644
index 00000000000..971a51f8a73
--- /dev/null
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTestJGit.java
@@ -0,0 +1,4 @@
+package org.jabref.gui.autosaveandbackup;
+
+public class BackupManagerTestJGit {
+}
From ae8583aefece24b31b7debdd89b1a890f9eba0ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sun, 1 Dec 2024 00:16:52 +0100
Subject: [PATCH 41/84] Fix the shutdown / usage of old BackupManager
---
src/main/java/org/jabref/gui/LibraryTab.java | 9 +--
.../BackupManagerGitTest.java | 75 ++++++++++---------
2 files changed, 43 insertions(+), 41 deletions(-)
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index 1cd6dfaaeef..6b1586ef9d9 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -42,7 +42,6 @@
import org.jabref.gui.autocompleter.SuggestionProvider;
import org.jabref.gui.autocompleter.SuggestionProviders;
import org.jabref.gui.autosaveandbackup.AutosaveManager;
-import org.jabref.gui.autosaveandbackup.BackupManager;
import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.collab.DatabaseChangeMonitor;
import org.jabref.gui.dialogs.AutosaveUiManager;
@@ -755,7 +754,7 @@ private boolean confirmClose() {
}
if (buttonType.equals(discardChanges)) {
- BackupManager.discardBackup(bibDatabaseContext, preferences.getFilePreferences().getBackupDirectory());
+ LOGGER.debug("Discarding changes");
return true;
}
@@ -803,9 +802,9 @@ private void onClosed(Event event) {
LOGGER.error("Problem when shutting down autosave manager", e);
}
try {
- BackupManager.shutdown(bibDatabaseContext,
- preferences.getFilePreferences().getBackupDirectory(),
- preferences.getFilePreferences().shouldCreateBackup());
+ BackupManagerGit.shutdown(bibDatabaseContext,
+ preferences.getFilePreferences().shouldCreateBackup(),
+ preferences.getFilePreferences().getBackupDirectory());
} catch (RuntimeException e) {
LOGGER.error("Problem when shutting down backup manager", e);
}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 01d0a1bbb56..55769095124 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -1,5 +1,14 @@
package org.jabref.gui.autosaveandbackup;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Answers;
+
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -16,15 +25,6 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-import org.mockito.Answers;
-
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -174,8 +174,8 @@ void testStartMethod() throws Exception {
Set runningInstances = (Set) runningInstancesField.get(null);
// Ensure the backup manager is added to the running instances
-// Assert: Verify the backup task is active
- assertTrue(runningInstances.contains(backupManager), "Backup manager should be added to running instances");
+ assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
+
// Clean up by shutting down the backup manager
BackupManagerGit.shutdown(bibDatabaseContext, false, databaseFile);
}
@@ -216,8 +216,7 @@ void testStartBackupTaskWithReflection() throws Exception {
Set runningInstances = (Set) runningInstancesField.get(null);
// Assert: Verify the backup task is active
- // Assert: Verify the backup task is active
- assertTrue(runningInstances.contains(backupManager), "Backup manager should be added to running instances");
+ assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
// Clean up
BackupManagerGit.shutdown(bibDatabaseContext, false, databaseFile);
@@ -228,7 +227,7 @@ void testRestoreBackup() throws Exception {
// Create multiple commits
ObjectId targetCommitId = null;
for (int i = 1; i <= 3; i++) {
- Path file = backupDir.resolve("file" + i + ".bib");
+ Path file = backupDir.resolve("file" + i + ".txt");
Files.writeString(file, "Content of file " + i);
git.add().addFilepattern(".").call();
RevCommit commit = git.commit().setMessage("Commit " + i).call();
@@ -239,30 +238,34 @@ void testRestoreBackup() throws Exception {
}
// Act: Call restoreBackup
- BackupManagerGit.restoreBackup(tempDir.resolve("restored.bib"), backupDir, targetCommitId);
- git.add().addFilepattern("restored.bib").call();
- git.commit().setMessage("Restored restored.bib from commit: " + targetCommitId.getName()).call();
+ BackupManagerGit.restoreBackup(tempDir.resolve("restored.txt"), backupDir, targetCommitId);
+
// Assert: Verify the repository has a new commit after restoration
try (RevWalk revWalk = new RevWalk(git.getRepository())) {
-
- // Assert: Ensure the file from the restored commit exists
+ RevCommit headCommit = revWalk.parseCommit(git.getRepository().resolve("HEAD"));
assertTrue(
- Files.exists(backupDir.resolve("file2.bib")),
- "File from the restored commit should be present"
+ headCommit.getShortMessage().contains("Restored content from commit: " + targetCommitId.getName()),
+ "A new commit should indicate the restoration"
);
+ }
- // Assert: Ensure files from later commits still exist
- assertTrue(
- Files.exists(backupDir.resolve("file3.bib")),
- "File from later commits should still exist after restoration"
- );
+ // Assert: Ensure the file from the restored commit exists
+ assertTrue(
+ Files.exists(backupDir.resolve("file2.txt")),
+ "File from the restored commit should be present"
+ );
- // Assert: Ensure earlier files still exist
- assertTrue(
- Files.exists(backupDir.resolve("file1.bib")),
- "File from earlier commits should still exist after restoration"
- );
- }
+ // Assert: Ensure files from later commits still exist
+ assertTrue(
+ Files.exists(backupDir.resolve("file3.txt")),
+ "File from later commits should still exist after restoration"
+ );
+
+ // Assert: Ensure earlier files still exist
+ assertTrue(
+ Files.exists(backupDir.resolve("file1.txt")),
+ "File from earlier commits should still exist after restoration"
+ );
}
@Test
@@ -359,18 +362,18 @@ void testRetrieveCommitDetails() throws Exception {
// Assert: Verify the content of the retrieved commit details
for (int i = 0; i < 5; i++) {
- BackupEntry commitInfo = commitDetails.get(i);
+ List commitInfo = (List) commitDetails.get(i);
RevCommit commit = commits.get(i);
// Verify commit ID
- assertEquals(commit.getName(), commitInfo.getId().name(), "Commit ID should match");
+ assertEquals(commit.getName(), commitInfo.get(0), "Commit ID should match");
// Verify commit size (this is a bit tricky, so just check it's a valid size string)
- String sizeFormatted = commitInfo.getSize();
+ String sizeFormatted = commitInfo.get(1);
assertTrue(sizeFormatted.contains("Ko") || sizeFormatted.contains("Mo"), "Commit size should be properly formatted");
// Verify commit date
- String commitDate = commitInfo.getDate();
+ String commitDate = commitInfo.get(2);
assertTrue(commitDate.contains(commit.getAuthorIdent().getWhen().toString()), "Commit date should match");
}
}
From 44f4e17a59d5ac4e71f2a0b0959f4ea37bd8192f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sun, 1 Dec 2024 00:31:54 +0100
Subject: [PATCH 42/84] correct path
---
.../java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 64457b6b6a7..adf4c5eba7e 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -68,7 +68,7 @@ public class BackupManagerGit {
changeFilter.registerListener(this);
// Ensure the backup directory exists
- Path backupDirPath = bibDatabaseContext.getDatabasePath().orElseThrow().getParent().resolve("backup");
+ Path backupDirPath = preferences.getFilePreferences().getBackupDirectory();
File backupDir = backupDirPath.toFile();
if (!backupDir.exists()) {
boolean dirCreated = backupDir.mkdirs();
From 19b0ef5a75e71fefd33740d4079a0ee8754fe99a Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Sun, 1 Dec 2024 01:56:35 +0100
Subject: [PATCH 43/84] attempt to remove useless original path in some methods
---
.../autosaveandbackup/BackupManagerGit.java | 44 +++++++++----------
.../BackupManagerGitTest.java | 38 +++++++---------
2 files changed, 37 insertions(+), 45 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index adf4c5eba7e..376f8496a7c 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -101,16 +101,15 @@ public class BackupManagerGit {
* @param bibDatabaseContext the BibDatabaseContext to be backed up
* @param entryTypesManager the BibEntryTypesManager
* @param preferences the CLI preferences
- * @param originalPath the original path of the file to be backed up
* @return the started BackupManagerGit instance
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
- public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
- LOGGER.info("Starting backup manager for file: {}", originalPath);
+ public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ LOGGER.info("Starting backup manager for file: {}", bibDatabaseContext.getDatabasePath());
BackupManagerGit backupManagerGit = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
+ backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
runningInstances.add(backupManagerGit);
return backupManagerGit;
}
@@ -135,16 +134,15 @@ public static void shutdown(BibDatabaseContext bibDatabaseContext, boolean creat
* Starts the backup task that periodically checks for changes and commits them to the Git repository.
*
* @param backupDir the backup directory
- * @param originalPath the original path of the file to be backed up
*/
- void startBackupTask(Path backupDir, Path originalPath) {
+ void startBackupTask(Path backupDir) {
executor.scheduleAtFixedRate(
() -> {
try {
- LOGGER.info("Starting backup task for file: {}", originalPath);
- performBackup(backupDir, originalPath);
- LOGGER.info("Backup task completed for file: {}", originalPath);
+ LOGGER.info("Starting backup task for file: {}", backupDir);
+ performBackup(backupDir);
+ LOGGER.info("Backup task completed for file: {}", backupDir);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
}
@@ -158,24 +156,23 @@ void startBackupTask(Path backupDir, Path originalPath) {
* Performs the backup by checking for changes and committing them to the Git repository.
*
* @param backupDir the backup directory
- * @param originalPath the original path of the file to be backed up
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
- protected void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
- LOGGER.info("Starting backup process for file: {}", originalPath);
+ protected void performBackup(Path backupDir) throws IOException, GitAPIException {
+ LOGGER.info("Starting backup process for file: {}", backupDir);
// Check if the file needs a backup by comparing it to the last commit
- boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir, originalPath);
+ boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir);
if (!needsBackup) {
- LOGGER.info("No changes detected. Backup not required for file: {}", originalPath);
+ LOGGER.info("No changes detected. Backup not required for file: {}", backupDir);
return;
}
- // Stage the file for commit
- Path relativePath = backupDir.relativize(originalPath); // Ensure relative path for Git
- git.add().addFilepattern(relativePath.toString().replace("\\", "/")).call();
+ // Stage the file for commit //Path relativePath = backupDir.relativize(originalPath); // Ensure relative path for Git
+
+ git.add().addFilepattern(backupDir.toString().replace("\\", "/")).call();
// Commit the staged changes
RevCommit commit = git.commit()
@@ -214,14 +211,13 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
/**
* Checks if there are differences between the original file and the backup.
*
- * @param originalPath the original path of the file
* @param backupDir the backup directory
* @return true if there are differences, false otherwise
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
- public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws IOException, GitAPIException {
+ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAPIException {
// Ensure Git repository exists
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
@@ -237,12 +233,12 @@ public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws
}
// Determine the path relative to the Git repository
- Path relativePath = backupDir.relativize(originalPath);
+ // Path relativePath = backupDir.relativize(originalPath);
// Attempt to retrieve the file content from the last commit
ObjectLoader loader;
try {
- loader = repository.open(repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/")));
+ loader = repository.open(repository.resolve("HEAD:" + backupDir.toString().replace("\\", "/")));
} catch (MissingObjectException e) {
// File not found in the last commit; assume it differs
return true;
@@ -252,11 +248,11 @@ public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws
String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
// Read the current content of the file
- if (!Files.exists(originalPath)) {
+ if (!Files.exists(backupDir)) {
// If the file doesn't exist in the working directory, it differs
return true;
}
- String currentContent = Files.readString(originalPath, StandardCharsets.UTF_8);
+ String currentContent = Files.readString(backupDir, StandardCharsets.UTF_8);
// Compare the current content to the committed content
return !currentContent.equals(committedContent);
@@ -404,7 +400,7 @@ private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath
if (createBackup) {
try {
// Ensure the backup is a recent one by performing the Git commit
- performBackup(backupDir, originalPath);
+ performBackup(backupDir);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during Git backup on shutdown", e);
}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 55769095124..2f340a04236 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -1,14 +1,5 @@
package org.jabref.gui.autosaveandbackup;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-import org.mockito.Answers;
-
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -25,6 +16,15 @@
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Answers;
+
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -91,7 +91,7 @@ void testBackupManagerInitializesGitRepository() throws Exception {
void testBackupGitDiffers_NoDifferences() throws Exception {
// Verify that there is no difference between the file and the last commit
Path testFile = backupDir.resolve("testfile.bib");
- boolean differs = BackupManagerGit.backupGitDiffers(backupDir, testFile);
+ boolean differs = BackupManagerGit.backupGitDiffers(backupDir);
assertFalse(differs, "Expected no difference between the file and the last commit");
}
@@ -101,7 +101,7 @@ void testBackupGitDiffers_WithDifferences() throws Exception {
Path testFile = backupDir.resolve("testfile.bib");
Files.writeString(testFile, "Modified content", StandardCharsets.UTF_8);
- boolean differs = BackupManagerGit.backupGitDiffers(backupDir, testFile);
+ boolean differs = BackupManagerGit.backupGitDiffers(backupDir);
assertTrue(differs, "Expected differences between the file and the last commit");
}
@@ -122,7 +122,7 @@ void testNoNewRepositoryCreated() throws Exception {
// Use backupGitDiffers to check if the backup differs
boolean createBackup;
if (bibDatabaseContext.getDatabasePath().isPresent()) {
- createBackup = BackupManagerGit.backupGitDiffers(bibDatabaseContext.getDatabasePath().get(), backupDir);
+ createBackup = BackupManagerGit.backupGitDiffers(backupDir);
} else {
fail("Database path is not present");
return; // Avoid further execution if the path is missing
@@ -159,8 +159,7 @@ void testStartMethod() throws Exception {
libraryTab,
bibDatabaseContext,
entryTypesManager,
- preferences,
- databaseFile
+ preferences
);
// Assert: Verify the outcomes
@@ -198,13 +197,12 @@ void testStartBackupTaskWithReflection() throws Exception {
libraryTab,
bibDatabaseContext,
entryTypesManager,
- preferences,
- databaseFile
+ preferences
);
// Act: Start the backup task
// private void startBackupTask(Path backupDir, Path originalPath)
- backupManager.startBackupTask(backupDirectory, databaseFile);
+ backupManager.startBackupTask(backupDirectory);
// Simulate passage of time
Thread.sleep(100);
@@ -297,8 +295,7 @@ void testRetrieveCommits() throws Exception {
libraryTab,
bibDatabaseContext,
entryTypesManager,
- preferences,
- databaseFile
+ preferences
);
List retrievedCommits = backupManager.retrieveCommits(backupDir, 5);
@@ -352,8 +349,7 @@ void testRetrieveCommitDetails() throws Exception {
libraryTab,
bibDatabaseContext,
entryTypesManager,
- preferences,
- databaseFile
+ preferences
);
List commitDetails = BackupManagerGit.retrieveCommitDetails(commits, backupDir);
From 7f89d89e41328c4ad74ee1704ae3d5f6b8b8d1f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sun, 1 Dec 2024 02:08:42 +0100
Subject: [PATCH 44/84] CORRECTING PATHS ! a clean launch (except for the 1st
exception)
---
src/main/java/org/jabref/gui/LibraryTab.java | 2 +-
.../autosaveandbackup/BackupManagerGit.java | 104 ++++++++++--------
.../jabref/gui/dialogs/BackupUIManager.java | 4 +-
.../importer/actions/OpenDatabaseAction.java | 10 +-
.../BackupManagerGitTest.java | 14 +--
5 files changed, 74 insertions(+), 60 deletions(-)
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index 6b1586ef9d9..181cc9fa5bb 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -374,7 +374,7 @@ public void installAutosaveManagerAndBackupManager() throws GitAPIException, IOE
autosaveManager.registerListener(new AutosaveUiManager(this, dialogService, preferences, entryTypesManager));
}
if (isDatabaseReadyForBackup(bibDatabaseContext) && preferences.getFilePreferences().shouldCreateBackup()) {
- BackupManagerGit.start(this, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class), preferences, bibDatabaseContext.getDatabasePath().get().getParent());
+ BackupManagerGit.start(this, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class), preferences);
}
}
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index adf4c5eba7e..2f4ae53ea29 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -16,6 +16,8 @@
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.jabref.gui.LibraryTab;
import org.jabref.gui.backup.BackupEntry;
@@ -69,6 +71,8 @@ public class BackupManagerGit {
// Ensure the backup directory exists
Path backupDirPath = preferences.getFilePreferences().getBackupDirectory();
+ LOGGER.info("backupDirPath" + backupDirPath);
+
File backupDir = backupDirPath.toFile();
if (!backupDir.exists()) {
boolean dirCreated = backupDir.mkdirs();
@@ -97,20 +101,18 @@ public class BackupManagerGit {
/**
* Starts a new BackupManagerGit instance and begins the backup task.
*
- * @param libraryTab the library tab
+ * @param libraryTab the library tab
* @param bibDatabaseContext the BibDatabaseContext to be backed up
- * @param entryTypesManager the BibEntryTypesManager
- * @param preferences the CLI preferences
- * @param originalPath the original path of the file to be backed up
+ * @param entryTypesManager the BibEntryTypesManager
+ * @param preferences the CLI preferences
* @return the started BackupManagerGit instance
- * @throws IOException if an I/O error occurs
+ * @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
- public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences, Path originalPath) throws IOException, GitAPIException {
- LOGGER.info("Starting backup manager for file: {}", originalPath);
+ public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
BackupManagerGit backupManagerGit = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), originalPath);
+ backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
runningInstances.add(backupManagerGit);
return backupManagerGit;
}
@@ -129,22 +131,21 @@ public static void shutdown(BibDatabaseContext bibDatabaseContext, boolean creat
// Remove the instances associated with the BibDatabaseContext after shutdown
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
+ LOGGER.info("Shut down backup manager for file: {}", originalPath);
}
/**
* Starts the backup task that periodically checks for changes and commits them to the Git repository.
*
* @param backupDir the backup directory
- * @param originalPath the original path of the file to be backed up
*/
- void startBackupTask(Path backupDir, Path originalPath) {
+ void startBackupTask(Path backupDir) {
+ LOGGER.info("Initializing backup task for directory: {} and file: {}", backupDir);
executor.scheduleAtFixedRate(
() -> {
try {
- LOGGER.info("Starting backup task for file: {}", originalPath);
- performBackup(backupDir, originalPath);
- LOGGER.info("Backup task completed for file: {}", originalPath);
+ performBackup(backupDir);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
}
@@ -152,30 +153,26 @@ void startBackupTask(Path backupDir, Path originalPath) {
DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
TimeUnit.SECONDS);
+ LOGGER.info("Backup task scheduled with a delay of {} seconds", DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS);
}
/**
* Performs the backup by checking for changes and committing them to the Git repository.
*
* @param backupDir the backup directory
- * @param originalPath the original path of the file to be backed up
- * @throws IOException if an I/O error occurs
+ * @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
- protected void performBackup(Path backupDir, Path originalPath) throws IOException, GitAPIException {
- LOGGER.info("Starting backup process for file: {}", originalPath);
-
+ protected void performBackup(Path backupDir) throws IOException, GitAPIException {
// Check if the file needs a backup by comparing it to the last commit
- boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir, originalPath);
+ boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir);
if (!needsBackup) {
- LOGGER.info("No changes detected. Backup not required for file: {}", originalPath);
return;
}
// Stage the file for commit
- Path relativePath = backupDir.relativize(originalPath); // Ensure relative path for Git
- git.add().addFilepattern(relativePath.toString().replace("\\", "/")).call();
+ git.add().addFilepattern(".").call();
// Commit the staged changes
RevCommit commit = git.commit()
@@ -187,12 +184,11 @@ protected void performBackup(Path backupDir, Path originalPath) throws IOExcepti
/**
* Restores the backup from the specified commit.
*
- * @param originalPath the original path of the file to be restored
* @param backupDir the backup directory
* @param objectId the commit ID to restore from
*/
- public static void restoreBackup(Path originalPath, Path backupDir, ObjectId objectId) {
+ public static void restoreBackup(Path backupDir, ObjectId objectId) {
try {
Git git = Git.open(backupDir.toFile());
@@ -214,14 +210,15 @@ public static void restoreBackup(Path originalPath, Path backupDir, ObjectId obj
/**
* Checks if there are differences between the original file and the backup.
*
- * @param originalPath the original path of the file
* @param backupDir the backup directory
* @return true if there are differences, false otherwise
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
- public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws IOException, GitAPIException {
+ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAPIException {
+ LOGGER.info("Checking if backup differs for directory: {}", backupDir);
+
// Ensure Git repository exists
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
@@ -233,34 +230,44 @@ public static boolean backupGitDiffers(Path backupDir, Path originalPath) throws
ObjectId headCommitId = repository.resolve("HEAD");
if (headCommitId == null) {
// No commits in the repository; assume the file differs
+ LOGGER.info("No commits found in the repository. Assuming the file differs.");
return true;
}
- // Determine the path relative to the Git repository
- Path relativePath = backupDir.relativize(originalPath);
+ // Iterate over files in the backup directory
+ try (Stream paths = Files.walk(backupDir)) {
+ for (Path path : paths.filter(Files::isRegularFile).collect(Collectors.toList())) {
+ // Determine the path relative to the Git repository
+ Path relativePath = backupDir.relativize(path);
+ LOGGER.info("Relative path: {}", relativePath);
- // Attempt to retrieve the file content from the last commit
- ObjectLoader loader;
- try {
- loader = repository.open(repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/")));
- } catch (MissingObjectException e) {
- // File not found in the last commit; assume it differs
- return true;
- }
+ // Attempt to retrieve the file content from the last commit
+ ObjectLoader loader;
+ try {
+ loader = repository.open(repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/")));
+ } catch (MissingObjectException e) {
+ // File not found in the last commit; assume it differs
+ LOGGER.info("File not found in the last commit. Assuming the file differs.");
+ return true;
+ }
- // Read the content from the last commit
- String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
+ // Read the content from the last commit
+ String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
- // Read the current content of the file
- if (!Files.exists(originalPath)) {
- // If the file doesn't exist in the working directory, it differs
- return true;
- }
- String currentContent = Files.readString(originalPath, StandardCharsets.UTF_8);
+ // Read the current content of the file
+ String currentContent = Files.readString(path, StandardCharsets.UTF_8);
- // Compare the current content to the committed content
- return !currentContent.equals(committedContent);
+ // Compare the current content to the committed content
+ if (!currentContent.equals(committedContent)) {
+ LOGGER.info("Current content of file {} differs from the committed content.", path);
+ return true;
+ }
+ }
+ }
}
+
+ LOGGER.info("All files in the backup directory match the committed content.");
+ return false;
}
/**
@@ -393,18 +400,21 @@ private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath
if (changeFilter != null) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
+ LOGGER.info("Shut down change filter for file: {}", originalPath);
}
// Shut down the executor if it's not already shut down
if (executor != null && !executor.isShutdown()) {
executor.shutdown();
+ LOGGER.info("Shut down backup task for file: {}", originalPath);
}
// If backup is requested, ensure that we perform the Git-based backup
if (createBackup) {
try {
// Ensure the backup is a recent one by performing the Git commit
- performBackup(backupDir, originalPath);
+ performBackup(backupDir);
+ LOGGER.info("Backup created on shutdown for file: {}", originalPath);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during Git backup on shutdown", e);
}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index c2f9687e86a..c344ccb9069 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -64,7 +64,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
try {
ObjectId commitId = BackupManagerGit.retrieveCommits(preferences.getFilePreferences().getBackupDirectory(), 1).getFirst().getId();
- BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory(), commitId);
+ BackupManagerGit.restoreBackup(preferences.getFilePreferences().getBackupDirectory(), commitId);
} catch (
IOException |
GitAPIException e
@@ -82,7 +82,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
if (recordBackupChoice.get().action() == BackupChoiceDialog.RESTORE_BACKUP) {
LOGGER.warn(recordBackupChoice.get().entry().getSize());
ObjectId commitId = recordBackupChoice.get().entry().getId();
- BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory(), commitId);
+ BackupManagerGit.restoreBackup(preferences.getFilePreferences().getBackupDirectory(), commitId);
return Optional.empty();
}
if (recordBackupChoice.get().action() == BackupChoiceDialog.REVIEW_BACKUP) {
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
index 2c3436e2476..bf6fb1ff84c 100644
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
@@ -96,7 +96,9 @@ public OpenDatabaseAction(LibraryTabContainer tabContainer,
public static void performPostOpenActions(ParserResult result, DialogService dialogService, CliPreferences preferences) {
for (GUIPostOpenAction action : OpenDatabaseAction.POST_OPEN_ACTIONS) {
+ LOGGER.info("Performing post open action: {}", action.getClass().getSimpleName());
if (action.isActionNecessary(result, dialogService, preferences)) {
+ LOGGER.info("Action is necessary");
action.performAction(result, dialogService, preferences);
}
}
@@ -104,6 +106,7 @@ public static void performPostOpenActions(ParserResult result, DialogService dia
@Override
public void execute() {
+ LOGGER.info("OpenDatabaseAction");
List filesToOpen = getFilesToOpen();
openFiles(new ArrayList<>(filesToOpen));
}
@@ -118,6 +121,7 @@ List getFilesToOpen() {
} catch (IllegalArgumentException e) {
// See https://github.com/JabRef/jabref/issues/10548 for details
// Rebuild a new config with the home directory
+ LOGGER.error("Error while opening file dialog", e);
FileDialogConfiguration homeDirectoryConfig = getFileDialogConfiguration(Directories.getUserDirectory());
filesToOpen = dialogService.showFileOpenDialogAndGetMultipleFiles(homeDirectoryConfig);
}
@@ -242,16 +246,19 @@ private void openTheFile(Path file) {
}
private ParserResult loadDatabase(Path file) throws Exception {
+ LOGGER.info("Opening {}", file);
Path fileToLoad = file.toAbsolutePath();
dialogService.notify(Localization.lang("Opening") + ": '" + file + "'");
+ LOGGER.info("Opening {}", fileToLoad);
preferences.getFilePreferences().setWorkingDirectory(fileToLoad.getParent());
Path backupDir = preferences.getFilePreferences().getBackupDirectory();
ParserResult parserResult = null;
- if (BackupManagerGit.backupGitDiffers(fileToLoad, backupDir)) {
+ if (BackupManagerGit.backupGitDiffers(backupDir)) {
// In case the backup differs, ask the user what to do.
+ LOGGER.info("Backup differs from saved file, ask the user what to do");
// In case the user opted for restoring a backup, the content of the backup is contained in parserResult.
parserResult = BackupUIManager.showRestoreBackupDialog(dialogService, fileToLoad, preferences, fileUpdateMonitor, undoManager, stateManager)
.orElse(null);
@@ -260,6 +267,7 @@ private ParserResult loadDatabase(Path file) throws Exception {
try {
if (parserResult == null) {
// No backup was restored, do the "normal" loading
+ LOGGER.info("No backup was restored, do the \"normal\" loading");
parserResult = OpenDatabase.loadDatabase(fileToLoad,
preferences.getImportFormatPreferences(),
fileUpdateMonitor);
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 55769095124..1fc8a24c237 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -159,8 +159,7 @@ void testStartMethod() throws Exception {
libraryTab,
bibDatabaseContext,
entryTypesManager,
- preferences,
- databaseFile
+ preferences
);
// Assert: Verify the outcomes
@@ -198,13 +197,12 @@ void testStartBackupTaskWithReflection() throws Exception {
libraryTab,
bibDatabaseContext,
entryTypesManager,
- preferences,
- databaseFile
+ preferences
);
// Act: Start the backup task
// private void startBackupTask(Path backupDir, Path originalPath)
- backupManager.startBackupTask(backupDirectory, databaseFile);
+ backupManager.startBackupTask(backupDirectory);
// Simulate passage of time
Thread.sleep(100);
@@ -297,8 +295,7 @@ void testRetrieveCommits() throws Exception {
libraryTab,
bibDatabaseContext,
entryTypesManager,
- preferences,
- databaseFile
+ preferences
);
List retrievedCommits = backupManager.retrieveCommits(backupDir, 5);
@@ -352,8 +349,7 @@ void testRetrieveCommitDetails() throws Exception {
libraryTab,
bibDatabaseContext,
entryTypesManager,
- preferences,
- databaseFile
+ preferences
);
List commitDetails = BackupManagerGit.retrieveCommitDetails(commits, backupDir);
From 119e5d038bfc259920664803f2fc06d6e3180619 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sun, 1 Dec 2024 02:26:13 +0100
Subject: [PATCH 45/84] Fixed the dialog of the 1st exception)
---
.../org/jabref/gui/autosaveandbackup/BackupManagerGit.java | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 2f4ae53ea29..066cd1c75e5 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -244,7 +244,12 @@ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAP
// Attempt to retrieve the file content from the last commit
ObjectLoader loader;
try {
- loader = repository.open(repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/")));
+ ObjectId objectId = repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/"));
+ if (objectId == null) {
+ LOGGER.info("Object ID for path {} is null. Assuming the file differs.", relativePath);
+ return true;
+ }
+ loader = repository.open(objectId);
} catch (MissingObjectException e) {
// File not found in the last commit; assume it differs
LOGGER.info("File not found in the last commit. Assuming the file differs.");
From a7d271b97491a03bc354e7518e17f09f55ec545e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sun, 1 Dec 2024 02:52:42 +0100
Subject: [PATCH 46/84] Fixed the infinite loops of commits
---
.../org/jabref/gui/autosaveandbackup/BackupManagerGit.java | 7 ++++++-
src/main/java/org/jabref/gui/dialogs/BackupUIManager.java | 1 +
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 066cd1c75e5..ff113303e7d 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -237,6 +237,11 @@ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAP
// Iterate over files in the backup directory
try (Stream paths = Files.walk(backupDir)) {
for (Path path : paths.filter(Files::isRegularFile).collect(Collectors.toList())) {
+ // Exclude internal Git files (e.g., .git directory files)
+ if (path.toString().contains(".git")) {
+ continue; // Skip Git internals like .git/config
+ }
+
// Determine the path relative to the Git repository
Path relativePath = backupDir.relativize(path);
LOGGER.info("Relative path: {}", relativePath);
@@ -271,7 +276,7 @@ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAP
}
}
- LOGGER.info("All files in the backup directory match the committed content.");
+ // No differences found
return false;
}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index c344ccb9069..5acaf654c3f 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -125,6 +125,7 @@ private static Optional showReviewBackupDialog(
BibDatabaseContext originalDatabase = originalParserResult.getDatabaseContext();
Path backupPath = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, preferences.getFilePreferences().getBackupDirectory()).orElseThrow();
+ LOGGER.info("Ligne 127, BackupUIManager, Loading backup database from {}", backupPath);
BibDatabaseContext backupDatabase = OpenDatabase.loadDatabase(backupPath, importFormatPreferences, new DummyFileUpdateMonitor()).getDatabaseContext();
DatabaseChangeResolverFactory changeResolverFactory = new DatabaseChangeResolverFactory(dialogService, originalDatabase, preferences);
From 96c3239c2a23d1b7814cb8aa820a08381c3db94d Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Sun, 1 Dec 2024 04:54:36 +0100
Subject: [PATCH 47/84] Reversed backup list
---
.../jabref/gui/backup/BackupChoiceDialog.java | 20 +------
.../jabref/gui/dialogs/BackupUIManager.java | 56 ++++++++++---------
2 files changed, 32 insertions(+), 44 deletions(-)
diff --git a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
index 01d42758fb4..d75f07a4535 100644
--- a/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
+++ b/src/main/java/org/jabref/gui/backup/BackupChoiceDialog.java
@@ -1,6 +1,5 @@
package org.jabref.gui.backup;
-import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
@@ -14,13 +13,9 @@
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
-import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.util.BaseDialog;
import org.jabref.logic.l10n.Localization;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.revwalk.RevCommit;
-
public class BackupChoiceDialog extends BaseDialog {
public static final ButtonType RESTORE_BACKUP = new ButtonType(Localization.lang("Restore from backup"), ButtonBar.ButtonData.OK_DONE);
public static final ButtonType IGNORE_BACKUP = new ButtonType(Localization.lang("Ignore backup"), ButtonBar.ButtonData.CANCEL_CLOSE);
@@ -32,7 +27,7 @@ public class BackupChoiceDialog extends BaseDialog {
@FXML
private final TableView backupTableView;
- public BackupChoiceDialog(Path originalPath, Path backupDir) {
+ public BackupChoiceDialog(Path backupDir, List backups) {
this.backupDir = backupDir;
setTitle(Localization.lang("Choose backup file"));
@@ -46,7 +41,7 @@ public BackupChoiceDialog(Path originalPath, Path backupDir) {
backupTableView = new TableView<>();
setupBackupTableView();
- fetchBackupData();
+ tableData.addAll(backups);
backupTableView.setItems(tableData);
@@ -75,15 +70,4 @@ private void setupBackupTableView() {
backupTableView.getColumns().addAll(dateColumn, sizeColumn, entriesColumn);
}
-
- private void fetchBackupData() {
- try {
- List commits = BackupManagerGit.retrieveCommits(backupDir, -1);
- tableData.addAll(BackupManagerGit.retrieveCommitDetails(commits, backupDir));
- } catch (
- IOException |
- GitAPIException e) {
- throw new RuntimeException(e);
- }
- }
}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 5acaf654c3f..4e328cc6c4d 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -15,6 +15,7 @@
import org.jabref.gui.autosaveandbackup.BackupManagerGit;
import org.jabref.gui.backup.BackupChoiceDialog;
import org.jabref.gui.backup.BackupChoiceDialogRecord;
+import org.jabref.gui.backup.BackupEntry;
import org.jabref.gui.backup.BackupResolverDialog;
import org.jabref.gui.collab.DatabaseChange;
import org.jabref.gui.collab.DatabaseChangeList;
@@ -36,6 +37,7 @@
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -60,35 +62,36 @@ public static Optional showRestoreBackupDialog(DialogService dialo
preferences.getExternalApplicationsPreferences(),
originalPath,
preferences.getFilePreferences().getBackupDirectory());
+
return actionOpt.flatMap(action -> {
- if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
- try {
- ObjectId commitId = BackupManagerGit.retrieveCommits(preferences.getFilePreferences().getBackupDirectory(), 1).getFirst().getId();
- BackupManagerGit.restoreBackup(preferences.getFilePreferences().getBackupDirectory(), commitId);
- } catch (
- IOException |
- GitAPIException e
- ) {
- throw new RuntimeException(e);
- }
- return Optional.empty();
- } else if (action == BackupResolverDialog.REVIEW_BACKUP) {
- return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
- } else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
- var recordBackupChoice = showBackupChoiceDialog(dialogService, originalPath, preferences);
- if (recordBackupChoice.isEmpty()) {
- return Optional.empty();
- }
- if (recordBackupChoice.get().action() == BackupChoiceDialog.RESTORE_BACKUP) {
- LOGGER.warn(recordBackupChoice.get().entry().getSize());
- ObjectId commitId = recordBackupChoice.get().entry().getId();
+ try {
+ List commits = BackupManagerGit.retrieveCommits(preferences.getFilePreferences().getBackupDirectory(), -1);
+ List backups = BackupManagerGit.retrieveCommitDetails(commits, preferences.getFilePreferences().getBackupDirectory()).reversed();
+ if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
+ ObjectId commitId = backups.getFirst().getId();
BackupManagerGit.restoreBackup(preferences.getFilePreferences().getBackupDirectory(), commitId);
return Optional.empty();
- }
- if (recordBackupChoice.get().action() == BackupChoiceDialog.REVIEW_BACKUP) {
- LOGGER.warn(recordBackupChoice.get().entry().getSize());
+ } else if (action == BackupResolverDialog.REVIEW_BACKUP) {
return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
+ } else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
+ var recordBackupChoice = showBackupChoiceDialog(dialogService, originalPath, preferences, backups);
+ if (recordBackupChoice.isEmpty()) {
+ return Optional.empty();
+ }
+ if (recordBackupChoice.get().action() == BackupChoiceDialog.RESTORE_BACKUP) {
+ LOGGER.warn(recordBackupChoice.get().entry().getSize());
+ ObjectId commitId = recordBackupChoice.get().entry().getId();
+ BackupManagerGit.restoreBackup(preferences.getFilePreferences().getBackupDirectory(), commitId);
+ return Optional.empty();
+ }
+ if (recordBackupChoice.get().action() == BackupChoiceDialog.REVIEW_BACKUP) {
+ LOGGER.warn(recordBackupChoice.get().entry().getSize());
+ return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
+ }
}
+ } catch (
+ GitAPIException | IOException e) {
+ throw new RuntimeException(e);
}
return Optional.empty();
});
@@ -104,9 +107,10 @@ private static Optional showBackupResolverDialog(DialogService dialo
private static Optional showBackupChoiceDialog(DialogService dialogService,
Path originalPath,
- GuiPreferences preferences) {
+ GuiPreferences preferences,
+ List backups) {
return UiTaskExecutor.runInJavaFXThread(
- () -> dialogService.showCustomDialogAndWait(new BackupChoiceDialog(originalPath, preferences.getFilePreferences().getBackupDirectory())));
+ () -> dialogService.showCustomDialogAndWait(new BackupChoiceDialog(preferences.getFilePreferences().getBackupDirectory(), backups)));
}
private static Optional showReviewBackupDialog(
From 5d0afe6838a8b66c1956440990ced560d9c42e28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sun, 1 Dec 2024 16:35:10 +0100
Subject: [PATCH 48/84] no more NULL for git initialization
---
.../autosaveandbackup/BackupManagerGit.java | 132 ++++++++++--------
.../importer/actions/OpenDatabaseAction.java | 10 ++
2 files changed, 82 insertions(+), 60 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index ff113303e7d..c5e03417cc8 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -16,7 +16,6 @@
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jabref.gui.LibraryTab;
@@ -55,7 +54,7 @@ public class BackupManagerGit {
private final CoarseChangeFilter changeFilter;
private final BibEntryTypesManager entryTypesManager;
private final LibraryTab libraryTab;
- private final Git git;
+ private static Git git;
private boolean needsBackup = false;
@@ -71,31 +70,44 @@ public class BackupManagerGit {
// Ensure the backup directory exists
Path backupDirPath = preferences.getFilePreferences().getBackupDirectory();
- LOGGER.info("backupDirPath" + backupDirPath);
+ LOGGER.info("Backup directory path: {}", backupDirPath);
File backupDir = backupDirPath.toFile();
- if (!backupDir.exists()) {
- boolean dirCreated = backupDir.mkdirs();
- if (dirCreated) {
- LOGGER.info("Created backup directory: " + backupDir);
- } else {
- LOGGER.error("Failed to create backup directory: " + backupDir);
- }
+ if (!backupDir.exists() && !backupDir.mkdirs()) {
+ LOGGER.error("Failed to create backup directory: {}", backupDir);
+ throw new IOException("Unable to create backup directory: " + backupDir);
}
- // Initialize Git repository in the backup directory
- FileRepositoryBuilder builder = new FileRepositoryBuilder();
- git = new Git(builder.setGitDir(new File(backupDir, ".git"))
- .readEnvironment()
- .findGitDir()
- .build());
+ // Ensure Git is initialized
+ ensureGitInitialized(backupDirPath);
+ }
+
+ private static void ensureGitInitialized(Path backupDir) throws IOException, GitAPIException {
+
+ // This method was created because the initialization of the Git object, when written in the constructor, was causing a NullPointerException
+ // because the first method called when loading the database is BackupGitdiffers
+
+ // Convert Path to File
+ File gitDir = new File(backupDir.toFile(), ".git");
- if (git.getRepository().getObjectDatabase().exists()) {
- LOGGER.info("Git repository already exists");
+ // Check if the `.git` directory exists
+ if (!gitDir.exists() || !gitDir.isDirectory()) {
+ LOGGER.info(".git directory not found in {}, initializing new Git repository.", backupDir);
+
+ // Initialize a new Git repository
+ Git.init().setDirectory(backupDir.toFile()).call();
+ LOGGER.info("Git repository successfully initialized in {}", backupDir);
} else {
- Git.init().setDirectory(backupDir).call(); // Explicitly set the directory
- LOGGER.info("Initialized new Git repository");
+ LOGGER.info("Existing Git repository found in {}", backupDir);
}
+
+ // Build the Git object
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
+ Repository repository = builder.setGitDir(gitDir)
+ .readEnvironment()
+ .findGitDir()
+ .build();
+ git = new Git(repository);
}
/**
@@ -178,7 +190,7 @@ protected void performBackup(Path backupDir) throws IOException, GitAPIException
RevCommit commit = git.commit()
.setMessage("Backup at " + Instant.now().toString())
.call();
- LOGGER.info("Backup committed with ID: {}", commit.getId().getName());
+ LOGGER.info("Backup committed in :" + backupDir + " with commit ID: " + commit.getName());
}
/**
@@ -219,64 +231,64 @@ public static void restoreBackup(Path backupDir, ObjectId objectId) {
public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAPIException {
LOGGER.info("Checking if backup differs for directory: {}", backupDir);
- // Ensure Git repository exists
- File repoDir = backupDir.toFile();
- Repository repository = new FileRepositoryBuilder()
- .setGitDir(new File(repoDir, ".git"))
- .build();
+ // Ensure the Git object is initialized
+ ensureGitInitialized(backupDir);
- try (Git git = new Git(repository)) {
- // Resolve HEAD commit
- ObjectId headCommitId = repository.resolve("HEAD");
- if (headCommitId == null) {
- // No commits in the repository; assume the file differs
- LOGGER.info("No commits found in the repository. Assuming the file differs.");
- return true;
- }
+ Repository repository = git.getRepository();
+ if (repository == null) {
+ LOGGER.error("Repository object is null. Cannot check for backup differences.");
+ throw new IllegalStateException("Repository object is not initialized.");
+ }
- // Iterate over files in the backup directory
- try (Stream paths = Files.walk(backupDir)) {
- for (Path path : paths.filter(Files::isRegularFile).collect(Collectors.toList())) {
- // Exclude internal Git files (e.g., .git directory files)
- if (path.toString().contains(".git")) {
- continue; // Skip Git internals like .git/config
- }
+ // Resolve HEAD commit
+ ObjectId headCommitId = repository.resolve("HEAD");
+ if (headCommitId == null) {
+ LOGGER.info("No commits found in the repository. Assuming the file differs.");
+ return true;
+ }
+
+ LOGGER.info("HEAD commit ID: {}", headCommitId.getName());
- // Determine the path relative to the Git repository
- Path relativePath = backupDir.relativize(path);
- LOGGER.info("Relative path: {}", relativePath);
+ try (Stream paths = Files.walk(backupDir)) {
+ for (Path path : paths.filter(Files::isRegularFile).toList()) {
+ // Skip internal Git files
+ if (path.toString().contains(".git")) {
+ continue;
+ }
+ // Determine relative path for Git
+ Path relativePath = backupDir.relativize(path);
+ String gitPath = relativePath.toString().replace("\\", "/");
+ LOGGER.info("Checking file: {}", gitPath);
+
+ try {
// Attempt to retrieve the file content from the last commit
- ObjectLoader loader;
- try {
- ObjectId objectId = repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/"));
- if (objectId == null) {
- LOGGER.info("Object ID for path {} is null. Assuming the file differs.", relativePath);
- return true;
- }
- loader = repository.open(objectId);
- } catch (MissingObjectException e) {
- // File not found in the last commit; assume it differs
- LOGGER.info("File not found in the last commit. Assuming the file differs.");
+ ObjectId objectId = repository.resolve("HEAD:" + gitPath);
+ if (objectId == null) {
+ LOGGER.info("File '{}' not found in the last commit. Assuming it differs.", gitPath);
return true;
}
- // Read the content from the last commit
+ // Load content from the Git object
+ ObjectLoader loader = repository.open(objectId);
String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
- // Read the current content of the file
+ // Load current file content
String currentContent = Files.readString(path, StandardCharsets.UTF_8);
- // Compare the current content to the committed content
+ // Compare contents
if (!currentContent.equals(committedContent)) {
- LOGGER.info("Current content of file {} differs from the committed content.", path);
+ LOGGER.info("Content differs for file: {}", path);
return true;
}
+ } catch (MissingObjectException e) {
+ LOGGER.info("File '{}' not found in the last commit. Assuming it differs.", gitPath);
+ return true;
}
}
}
- // No differences found
+ LOGGER.info("No differences found in backup.");
return false;
}
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
index bf6fb1ff84c..2c2b14f5f27 100644
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
@@ -255,6 +255,16 @@ private ParserResult loadDatabase(Path file) throws Exception {
preferences.getFilePreferences().setWorkingDirectory(fileToLoad.getParent());
Path backupDir = preferences.getFilePreferences().getBackupDirectory();
+ // To debug
+ if (!Files.exists(backupDir)) {
+ LOGGER.error("Backup directory does not exist: {}", backupDir);
+ throw new IOException("Backup directory not found: " + backupDir);
+ }
+ if (!Files.isReadable(backupDir)) {
+ LOGGER.error("Backup directory is not readable: {}", backupDir);
+ throw new IOException("Cannot read from backup directory: " + backupDir);
+ }
+
ParserResult parserResult = null;
if (BackupManagerGit.backupGitDiffers(backupDir)) {
// In case the backup differs, ask the user what to do.
From 7a48131c610d8e1db884d75cfea8e52ae77838cb Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Sun, 1 Dec 2024 17:35:27 +0100
Subject: [PATCH 49/84] resolving prblm
---
.../autosaveandbackup/BackupManagerGit.java | 112 +++++++++---------
.../importer/actions/OpenDatabaseAction.java | 2 +-
.../BackupManagerGitTest.java | 8 +-
3 files changed, 61 insertions(+), 61 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index c5e03417cc8..b12d0bfb0d9 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -16,7 +16,6 @@
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
import org.jabref.gui.LibraryTab;
import org.jabref.gui.backup.BackupEntry;
@@ -29,7 +28,6 @@
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
-import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
@@ -47,6 +45,7 @@ public class BackupManagerGit {
private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
private static Set runningInstances = new HashSet();
+ private static Git git;
private final BibDatabaseContext bibDatabaseContext;
private final CliPreferences preferences;
@@ -54,7 +53,8 @@ public class BackupManagerGit {
private final CoarseChangeFilter changeFilter;
private final BibEntryTypesManager entryTypesManager;
private final LibraryTab libraryTab;
- private static Git git;
+
+
private boolean needsBackup = false;
@@ -67,6 +67,7 @@ public class BackupManagerGit {
changeFilter = new CoarseChangeFilter(bibDatabaseContext);
changeFilter.registerListener(this);
+ LOGGER.info("maysmm i l file: {}", bibDatabaseContext.getDatabasePath().orElseThrow());
// Ensure the backup directory exists
Path backupDirPath = preferences.getFilePreferences().getBackupDirectory();
@@ -124,7 +125,7 @@ private static void ensureGitInitialized(Path backupDir) throws IOException, Git
public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
BackupManagerGit backupManagerGit = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
+ backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), bibDatabaseContext.getDatabasePath().orElseThrow());
runningInstances.add(backupManagerGit);
return backupManagerGit;
}
@@ -152,12 +153,12 @@ public static void shutdown(BibDatabaseContext bibDatabaseContext, boolean creat
* @param backupDir the backup directory
*/
- void startBackupTask(Path backupDir) {
- LOGGER.info("Initializing backup task for directory: {} and file: {}", backupDir);
+ void startBackupTask(Path backupDir, Path bibPath) {
+ LOGGER.info("Initializing backup task for directory: {} and file: {}", backupDir, bibPath);
executor.scheduleAtFixedRate(
() -> {
try {
- performBackup(backupDir);
+ performBackup(backupDir, bibPath);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
}
@@ -176,9 +177,9 @@ void startBackupTask(Path backupDir) {
* @throws GitAPIException if a Git API error occurs
*/
- protected void performBackup(Path backupDir) throws IOException, GitAPIException {
+ protected void performBackup(Path backupDir, Path bibPath) throws IOException, GitAPIException {
// Check if the file needs a backup by comparing it to the last commit
- boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir);
+ boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir, bibPath);
if (!needsBackup) {
return;
}
@@ -228,7 +229,8 @@ public static void restoreBackup(Path backupDir, ObjectId objectId) {
* @throws GitAPIException if a Git API error occurs
*/
- public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAPIException {
+ @SuppressWarnings("checkstyle:RegexpMultiline")
+ public static boolean backupGitDiffers(Path backupDir, Path bibPath) throws IOException, GitAPIException {
LOGGER.info("Checking if backup differs for directory: {}", backupDir);
// Ensure the Git object is initialized
@@ -239,57 +241,55 @@ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAP
LOGGER.error("Repository object is null. Cannot check for backup differences.");
throw new IllegalStateException("Repository object is not initialized.");
}
+ try {
- // Resolve HEAD commit
- ObjectId headCommitId = repository.resolve("HEAD");
- if (headCommitId == null) {
- LOGGER.info("No commits found in the repository. Assuming the file differs.");
- return true;
- }
-
- LOGGER.info("HEAD commit ID: {}", headCommitId.getName());
-
- try (Stream paths = Files.walk(backupDir)) {
- for (Path path : paths.filter(Files::isRegularFile).toList()) {
- // Skip internal Git files
- if (path.toString().contains(".git")) {
- continue;
- }
-
- // Determine relative path for Git
- Path relativePath = backupDir.relativize(path);
- String gitPath = relativePath.toString().replace("\\", "/");
- LOGGER.info("Checking file: {}", gitPath);
-
- try {
- // Attempt to retrieve the file content from the last commit
- ObjectId objectId = repository.resolve("HEAD:" + gitPath);
- if (objectId == null) {
- LOGGER.info("File '{}' not found in the last commit. Assuming it differs.", gitPath);
- return true;
- }
+ // Compute the relative path for the .bib file
+ Path gitWorkTree = repository.getWorkTree().toPath();
+ Path relativePath = gitWorkTree.relativize(bibPath);
+ String gitPath = relativePath.toString().replace("\\", "/");
- // Load content from the Git object
- ObjectLoader loader = repository.open(objectId);
- String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
+ LOGGER.info("Comparing file: {}", gitPath);
+ LOGGER.info("repository: {}", repository.getWorkTree().toPath());
- // Load current file content
- String currentContent = Files.readString(path, StandardCharsets.UTF_8);
+ // Resolve the file in the latest commit
+ ObjectId objectId = repository.resolve("HEAD:" + gitPath);
+ if (objectId == null) {
+ LOGGER.info("File '{}' not found in the last commit. Assuming it differs.", gitPath);
+ return true;
+ }
- // Compare contents
- if (!currentContent.equals(committedContent)) {
- LOGGER.info("Content differs for file: {}", path);
- return true;
- }
- } catch (MissingObjectException e) {
- LOGGER.info("File '{}' not found in the last commit. Assuming it differs.", gitPath);
- return true;
- }
+ // Load content from the Git repository
+ ObjectLoader loader = repository.open(objectId);
+ String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
+ LOGGER.info(committedContent);
+
+ // Load current content from the filesystem
+ String currentContent = Files.readString(bibPath, StandardCharsets.UTF_8);
+ LOGGER.info(currentContent);
+ // Normalize whitespace and line endings
+ String normalizedCommittedContent = committedContent
+ .trim()
+ .replaceAll("\r\n", "\n")
+ .replaceAll("[ \\t]+", " ") // Replace multiple spaces with single space
+ .replaceAll("\n+", "\n"); // Remove multiple blank lines
+ LOGGER.info("normalizedCommittedContent: {} ", normalizedCommittedContent);
+ String normalizedCurrentContent = currentContent
+ .trim()
+ .replaceAll("\r\n", "\n")
+ .replaceAll("[ \\t]+", " ")
+ .replaceAll("\n+", "\n");
+ LOGGER.info("normalizedCurrentContent: {} ", normalizedCurrentContent);
+ if (!normalizedCurrentContent.equals(normalizedCommittedContent)) {
+ LOGGER.info("Content differs after normalization");
+ return true;
+ } else {
+ LOGGER.info("No differences found for file: {}", bibPath);
}
+ } catch (Exception e) {
+ LOGGER.error("Error while comparing file '{}': {}", bibPath, e.getMessage());
+ return true; // Assume the file differs if an error occurs
}
-
- LOGGER.info("No differences found in backup.");
- return false;
+ return false; // No differences found
}
/**
@@ -435,7 +435,7 @@ private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath
if (createBackup) {
try {
// Ensure the backup is a recent one by performing the Git commit
- performBackup(backupDir);
+ performBackup(backupDir, originalPath);
LOGGER.info("Backup created on shutdown for file: {}", originalPath);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during Git backup on shutdown", e);
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
index 2c2b14f5f27..436842555fc 100644
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
@@ -266,7 +266,7 @@ private ParserResult loadDatabase(Path file) throws Exception {
}
ParserResult parserResult = null;
- if (BackupManagerGit.backupGitDiffers(backupDir)) {
+ if (BackupManagerGit.backupGitDiffers(backupDir, fileToLoad)) {
// In case the backup differs, ask the user what to do.
LOGGER.info("Backup differs from saved file, ask the user what to do");
// In case the user opted for restoring a backup, the content of the backup is contained in parserResult.
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 2f340a04236..cff3e7545c0 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -91,7 +91,7 @@ void testBackupManagerInitializesGitRepository() throws Exception {
void testBackupGitDiffers_NoDifferences() throws Exception {
// Verify that there is no difference between the file and the last commit
Path testFile = backupDir.resolve("testfile.bib");
- boolean differs = BackupManagerGit.backupGitDiffers(backupDir);
+ boolean differs = BackupManagerGit.backupGitDiffers(backupDir, testFile);
assertFalse(differs, "Expected no difference between the file and the last commit");
}
@@ -101,7 +101,7 @@ void testBackupGitDiffers_WithDifferences() throws Exception {
Path testFile = backupDir.resolve("testfile.bib");
Files.writeString(testFile, "Modified content", StandardCharsets.UTF_8);
- boolean differs = BackupManagerGit.backupGitDiffers(backupDir);
+ boolean differs = BackupManagerGit.backupGitDiffers(backupDir, testFile);
assertTrue(differs, "Expected differences between the file and the last commit");
}
@@ -122,7 +122,7 @@ void testNoNewRepositoryCreated() throws Exception {
// Use backupGitDiffers to check if the backup differs
boolean createBackup;
if (bibDatabaseContext.getDatabasePath().isPresent()) {
- createBackup = BackupManagerGit.backupGitDiffers(backupDir);
+ createBackup = BackupManagerGit.backupGitDiffers(backupDir, bibDatabaseContext.getDatabasePath().get());
} else {
fail("Database path is not present");
return; // Avoid further execution if the path is missing
@@ -202,7 +202,7 @@ void testStartBackupTaskWithReflection() throws Exception {
// Act: Start the backup task
// private void startBackupTask(Path backupDir, Path originalPath)
- backupManager.startBackupTask(backupDirectory);
+ backupManager.startBackupTask(backupDirectory, bibDatabaseContext.getDatabasePath().orElse());
// Simulate passage of time
Thread.sleep(100);
From 47d52381e465f146997eab9e4212cfb0129aa79e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sun, 1 Dec 2024 18:37:27 +0100
Subject: [PATCH 50/84] Copying files to the backupdir logic
---
.../autosaveandbackup/BackupManagerGit.java | 165 +++++++++++-------
.../importer/actions/OpenDatabaseAction.java | 2 +-
.../BackupManagerGitTest.java | 6 +-
3 files changed, 105 insertions(+), 68 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index b12d0bfb0d9..2117f9e6cb9 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -7,6 +7,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
@@ -16,6 +17,7 @@
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
import org.jabref.gui.LibraryTab;
import org.jabref.gui.backup.BackupEntry;
@@ -28,6 +30,7 @@
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
@@ -45,7 +48,6 @@ public class BackupManagerGit {
private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
private static Set runningInstances = new HashSet();
- private static Git git;
private final BibDatabaseContext bibDatabaseContext;
private final CliPreferences preferences;
@@ -53,13 +55,13 @@ public class BackupManagerGit {
private final CoarseChangeFilter changeFilter;
private final BibEntryTypesManager entryTypesManager;
private final LibraryTab libraryTab;
-
-
+ private static Git git;
private boolean needsBackup = false;
BackupManagerGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
this.bibDatabaseContext = bibDatabaseContext;
+ LOGGER.info("Backup manager initialized for file: {}", bibDatabaseContext.getDatabasePath().orElseThrow());
this.entryTypesManager = entryTypesManager;
this.preferences = preferences;
this.executor = new ScheduledThreadPoolExecutor(2);
@@ -67,20 +69,32 @@ public class BackupManagerGit {
changeFilter = new CoarseChangeFilter(bibDatabaseContext);
changeFilter.registerListener(this);
- LOGGER.info("maysmm i l file: {}", bibDatabaseContext.getDatabasePath().orElseThrow());
// Ensure the backup directory exists
Path backupDirPath = preferences.getFilePreferences().getBackupDirectory();
LOGGER.info("Backup directory path: {}", backupDirPath);
+ // Ensure Git is initialized
+ ensureGitInitialized(backupDirPath);
+
File backupDir = backupDirPath.toFile();
if (!backupDir.exists() && !backupDir.mkdirs()) {
LOGGER.error("Failed to create backup directory: {}", backupDir);
throw new IOException("Unable to create backup directory: " + backupDir);
}
- // Ensure Git is initialized
- ensureGitInitialized(backupDirPath);
+ // Get the path of the BibDatabase file
+ Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
+
+ // Copy the database file to the backup directory
+ Path backupFilePath = backupDirPath.resolve(dbFile.getFileName());
+ try {
+ Files.copy(dbFile, backupFilePath, StandardCopyOption.REPLACE_EXISTING);
+ LOGGER.info("Database file copied to backup directory: {}", backupFilePath);
+ } catch (IOException e) {
+ LOGGER.error("Failed to copy database file to backup directory", e);
+ throw new IOException("Error copying database file to backup directory", e);
+ }
}
private static void ensureGitInitialized(Path backupDir) throws IOException, GitAPIException {
@@ -125,7 +139,7 @@ private static void ensureGitInitialized(Path backupDir) throws IOException, Git
public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
BackupManagerGit backupManagerGit = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), bibDatabaseContext.getDatabasePath().orElseThrow());
+ backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), bibDatabaseContext);
runningInstances.add(backupManagerGit);
return backupManagerGit;
}
@@ -153,12 +167,25 @@ public static void shutdown(BibDatabaseContext bibDatabaseContext, boolean creat
* @param backupDir the backup directory
*/
- void startBackupTask(Path backupDir, Path bibPath) {
- LOGGER.info("Initializing backup task for directory: {} and file: {}", backupDir, bibPath);
+ void startBackupTask(Path backupDir, BibDatabaseContext bibDatabaseContext) {
+ LOGGER.info("Initializing backup task for directory: {} and file: {}", backupDir);
executor.scheduleAtFixedRate(
() -> {
try {
- performBackup(backupDir, bibPath);
+ // Copy the database file to the backup directory
+ Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
+
+ // Copy the database file to the backup directory (overwriting any existing file)
+ Path backupFilePath = backupDir.resolve(dbFile.getFileName());
+ try {
+ Files.copy(dbFile, backupFilePath, StandardCopyOption.REPLACE_EXISTING);
+ LOGGER.info("Database file copied to backup directory: {}", backupFilePath);
+ } catch (IOException e) {
+ LOGGER.error("Failed to copy database file to backup directory", e);
+ throw new IOException("Error copying database file to backup directory", e);
+ }
+
+ performBackup(backupDir);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
}
@@ -177,9 +204,10 @@ void startBackupTask(Path backupDir, Path bibPath) {
* @throws GitAPIException if a Git API error occurs
*/
- protected void performBackup(Path backupDir, Path bibPath) throws IOException, GitAPIException {
+ protected void performBackup(Path backupDir) throws IOException, GitAPIException {
+
// Check if the file needs a backup by comparing it to the last commit
- boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir, bibPath);
+ boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir);
if (!needsBackup) {
return;
}
@@ -229,67 +257,76 @@ public static void restoreBackup(Path backupDir, ObjectId objectId) {
* @throws GitAPIException if a Git API error occurs
*/
- @SuppressWarnings("checkstyle:RegexpMultiline")
- public static boolean backupGitDiffers(Path backupDir, Path bibPath) throws IOException, GitAPIException {
+ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAPIException {
+ // Ensure the Git repository exists
LOGGER.info("Checking if backup differs for directory: {}", backupDir);
- // Ensure the Git object is initialized
- ensureGitInitialized(backupDir);
+ // Open the Git repository located in the backup directory
+ Repository repository = openGitRepository(backupDir);
- Repository repository = git.getRepository();
- if (repository == null) {
- LOGGER.error("Repository object is null. Cannot check for backup differences.");
- throw new IllegalStateException("Repository object is not initialized.");
+ // Get the HEAD commit to compare with
+ ObjectId headCommitId = repository.resolve("HEAD");
+ if (headCommitId == null) {
+ LOGGER.info("No commits found in the repository. Assuming the file differs.");
+ return true;
}
- try {
+ LOGGER.info("HEAD commit ID: {}", headCommitId.getName());
+
+ // Iterate over the files in the backup directory to check if they differ from the repository
+ try (Stream paths = Files.walk(backupDir)) {
+ for (Path path : paths.filter(Files::isRegularFile).toList()) {
+ // Ignore non-.bib files (e.g., .DS_Store)
+ if (!path.toString().endsWith(".bib")) {
+ LOGGER.info("Ignoring non-.bib file: {}", path);
+ continue; // Skip .bib files
+ }
+
+ // Skip .git directory files
+ if (path.toString().contains(".git")) {
+ continue;
+ }
- // Compute the relative path for the .bib file
- Path gitWorkTree = repository.getWorkTree().toPath();
- Path relativePath = gitWorkTree.relativize(bibPath);
- String gitPath = relativePath.toString().replace("\\", "/");
+ // Calculate the relative path in the Git repository
+ Path relativePath = backupDir.relativize(path);
+ LOGGER.info("Checking file: {}", relativePath);
- LOGGER.info("Comparing file: {}", gitPath);
- LOGGER.info("repository: {}", repository.getWorkTree().toPath());
+ try {
+ // Check if the file exists in the latest commit
+ ObjectId objectId = repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/"));
+ if (objectId == null) {
+ LOGGER.info("File not found in the latest commit: {}. Assuming it differs.", relativePath);
+ return true;
+ }
- // Resolve the file in the latest commit
- ObjectId objectId = repository.resolve("HEAD:" + gitPath);
- if (objectId == null) {
- LOGGER.info("File '{}' not found in the last commit. Assuming it differs.", gitPath);
- return true;
- }
+ // Compare the content of the file in the Git repository with the current file
+ ObjectLoader loader = repository.open(objectId);
+ String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
+ String currentContent = Files.readString(path, StandardCharsets.UTF_8);
- // Load content from the Git repository
- ObjectLoader loader = repository.open(objectId);
- String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
- LOGGER.info(committedContent);
-
- // Load current content from the filesystem
- String currentContent = Files.readString(bibPath, StandardCharsets.UTF_8);
- LOGGER.info(currentContent);
- // Normalize whitespace and line endings
- String normalizedCommittedContent = committedContent
- .trim()
- .replaceAll("\r\n", "\n")
- .replaceAll("[ \\t]+", " ") // Replace multiple spaces with single space
- .replaceAll("\n+", "\n"); // Remove multiple blank lines
- LOGGER.info("normalizedCommittedContent: {} ", normalizedCommittedContent);
- String normalizedCurrentContent = currentContent
- .trim()
- .replaceAll("\r\n", "\n")
- .replaceAll("[ \\t]+", " ")
- .replaceAll("\n+", "\n");
- LOGGER.info("normalizedCurrentContent: {} ", normalizedCurrentContent);
- if (!normalizedCurrentContent.equals(normalizedCommittedContent)) {
- LOGGER.info("Content differs after normalization");
- return true;
- } else {
- LOGGER.info("No differences found for file: {}", bibPath);
+ // If the contents differ, return true
+ if (!currentContent.equals(committedContent)) {
+ LOGGER.info("Content differs for file: {}", relativePath);
+ return true;
+ }
+ } catch (MissingObjectException e) {
+ // If the file is missing from the commit, assume it differs
+ LOGGER.info("File not found in the latest commit: {}. Assuming it differs.", relativePath);
+ return true;
+ }
}
- } catch (Exception e) {
- LOGGER.error("Error while comparing file '{}': {}", bibPath, e.getMessage());
- return true; // Assume the file differs if an error occurs
}
- return false; // No differences found
+
+ LOGGER.info("No differences found in the backup.");
+ return false; // No differences found
+ }
+
+ private static Repository openGitRepository(Path backupDir) throws IOException {
+ FileRepositoryBuilder builder = new FileRepositoryBuilder();
+ // Initialize Git repository from the backup directory
+ return builder.setGitDir(new File(backupDir.toFile(), ".git"))
+ .readEnvironment()
+ .findGitDir()
+ .build();
}
/**
@@ -435,7 +472,7 @@ private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath
if (createBackup) {
try {
// Ensure the backup is a recent one by performing the Git commit
- performBackup(backupDir, originalPath);
+ performBackup(backupDir);
LOGGER.info("Backup created on shutdown for file: {}", originalPath);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during Git backup on shutdown", e);
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
index 436842555fc..2c2b14f5f27 100644
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
@@ -266,7 +266,7 @@ private ParserResult loadDatabase(Path file) throws Exception {
}
ParserResult parserResult = null;
- if (BackupManagerGit.backupGitDiffers(backupDir, fileToLoad)) {
+ if (BackupManagerGit.backupGitDiffers(backupDir)) {
// In case the backup differs, ask the user what to do.
LOGGER.info("Backup differs from saved file, ask the user what to do");
// In case the user opted for restoring a backup, the content of the backup is contained in parserResult.
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index cff3e7545c0..c20698962e6 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -91,7 +91,7 @@ void testBackupManagerInitializesGitRepository() throws Exception {
void testBackupGitDiffers_NoDifferences() throws Exception {
// Verify that there is no difference between the file and the last commit
Path testFile = backupDir.resolve("testfile.bib");
- boolean differs = BackupManagerGit.backupGitDiffers(backupDir, testFile);
+ boolean differs = BackupManagerGit.backupGitDiffers(backupDir);
assertFalse(differs, "Expected no difference between the file and the last commit");
}
@@ -101,7 +101,7 @@ void testBackupGitDiffers_WithDifferences() throws Exception {
Path testFile = backupDir.resolve("testfile.bib");
Files.writeString(testFile, "Modified content", StandardCharsets.UTF_8);
- boolean differs = BackupManagerGit.backupGitDiffers(backupDir, testFile);
+ boolean differs = BackupManagerGit.backupGitDiffers(backupDir;
assertTrue(differs, "Expected differences between the file and the last commit");
}
@@ -122,7 +122,7 @@ void testNoNewRepositoryCreated() throws Exception {
// Use backupGitDiffers to check if the backup differs
boolean createBackup;
if (bibDatabaseContext.getDatabasePath().isPresent()) {
- createBackup = BackupManagerGit.backupGitDiffers(backupDir, bibDatabaseContext.getDatabasePath().get());
+ createBackup = BackupManagerGit.backupGitDiffers(backupDir);
} else {
fail("Database path is not present");
return; // Avoid further execution if the path is missing
From e881ce4e31b2e2941fe38b1a8285d9f864ec2c4a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sun, 1 Dec 2024 21:21:47 +0100
Subject: [PATCH 51/84] Add listeners
---
.../jabref/gui/autosaveandbackup/BackupManager.java | 6 ++----
.../gui/autosaveandbackup/BackupManagerGit.java | 13 ++++++++-----
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
index 8e468418908..e6458520653 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
@@ -41,7 +41,6 @@
import org.jabref.model.metadata.SaveOrder;
import org.jabref.model.metadata.SelfContainedSaveOrder;
-import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -337,8 +336,7 @@ private void logIfCritical(Path backupPath, IOException e) {
}
}
- @Subscribe
- public synchronized void listen(@SuppressWarnings("unused") BibDatabaseContextChangedEvent event) {
+ public synchronized void listen(BibDatabaseContextChangedEvent event) {
if (!event.isFilteredOut()) {
this.needsBackup = true;
}
@@ -356,7 +354,7 @@ private void startBackupTask(Path backupDir) {
TimeUnit.SECONDS);
}
// La méthode fillQueue(backupDir) est définie dans le code et son rôle est de lister et d'ajouter
-// les fichiers de sauvegarde existants dans une file d'attente,
+ // les fichiers de sauvegarde existants dans une file d'attente,
private void fillQueue(Path backupDir) {
if (!Files.exists(backupDir)) {
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 2117f9e6cb9..cef6fd5a827 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -24,6 +24,7 @@
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.util.CoarseChangeFilter;
import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.database.event.BibDatabaseContextChangedEvent;
import org.jabref.model.entry.BibEntryTypesManager;
import org.eclipse.jgit.api.Git;
@@ -205,9 +206,6 @@ void startBackupTask(Path backupDir, BibDatabaseContext bibDatabaseContext) {
*/
protected void performBackup(Path backupDir) throws IOException, GitAPIException {
-
- // Check if the file needs a backup by comparing it to the last commit
- boolean needsBackup = BackupManagerGit.backupGitDiffers(backupDir);
if (!needsBackup) {
return;
}
@@ -222,6 +220,12 @@ protected void performBackup(Path backupDir) throws IOException, GitAPIException
LOGGER.info("Backup committed in :" + backupDir + " with commit ID: " + commit.getName());
}
+ public synchronized void listen(BibDatabaseContextChangedEvent event) {
+ if (!event.isFilteredOut()) {
+ this.needsBackup = true;
+ }
+ }
+
/**
* Restores the backup from the specified commit.
*
@@ -249,7 +253,7 @@ public static void restoreBackup(Path backupDir, ObjectId objectId) {
}
/**
- * Checks if there are differences between the original file and the backup.
+ * Checks if there are differences between the files in the directory and the last commit.
*
* @param backupDir the backup directory
* @return true if there are differences, false otherwise
@@ -277,7 +281,6 @@ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAP
for (Path path : paths.filter(Files::isRegularFile).toList()) {
// Ignore non-.bib files (e.g., .DS_Store)
if (!path.toString().endsWith(".bib")) {
- LOGGER.info("Ignoring non-.bib file: {}", path);
continue; // Skip .bib files
}
From 02fa1d1dc1f9b13c9aefa0b0f4c195dbc279ac5c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Sun, 1 Dec 2024 22:15:14 +0100
Subject: [PATCH 52/84] Listeners detect changes but don't set needsBackup to
true (I don't know how they consider a change major or not) + commit are
compared and staged correctly but after manually saving the library
---
.../autosaveandbackup/BackupManagerGit.java | 19 +-
.../BackupManagerGitTest.java | 383 ------------------
2 files changed, 17 insertions(+), 385 deletions(-)
delete mode 100644 src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index cef6fd5a827..89f8cefc246 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -50,13 +50,14 @@ public class BackupManagerGit {
private static Set runningInstances = new HashSet();
+ private static Git git;
+
private final BibDatabaseContext bibDatabaseContext;
private final CliPreferences preferences;
private final ScheduledThreadPoolExecutor executor;
private final CoarseChangeFilter changeFilter;
private final BibEntryTypesManager entryTypesManager;
private final LibraryTab libraryTab;
- private static Git git;
private boolean needsBackup = false;
@@ -206,12 +207,23 @@ void startBackupTask(Path backupDir, BibDatabaseContext bibDatabaseContext) {
*/
protected void performBackup(Path backupDir) throws IOException, GitAPIException {
- if (!needsBackup) {
+
+ boolean needsCommit = backupGitDiffers(backupDir);
+
+ if (!needsBackup && !needsCommit) {
+ LOGGER.info("No changes detected, beacuse needsBackup is :" + needsBackup + " and needsCommit is :" + needsCommit);
return;
}
+ if (needsBackup) {
+ LOGGER.info("Backup needed, because needsBackup is :" + needsBackup);
+ } else {
+ LOGGER.info("Backup needed, because needsCommit is :" + needsCommit);
+ }
+
// Stage the file for commit
git.add().addFilepattern(".").call();
+ LOGGER.info("Staged changes for backup in directory: {}", backupDir);
// Commit the staged changes
RevCommit commit = git.commit()
@@ -222,6 +234,7 @@ protected void performBackup(Path backupDir) throws IOException, GitAPIException
public synchronized void listen(BibDatabaseContextChangedEvent event) {
if (!event.isFilteredOut()) {
+ LOGGER.info("Change detected/LISTENED in file: {}", bibDatabaseContext.getDatabasePath().orElseThrow());
this.needsBackup = true;
}
}
@@ -238,6 +251,8 @@ public static void restoreBackup(Path backupDir, ObjectId objectId) {
Git git = Git.open(backupDir.toFile());
git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
+ LOGGER.info("checkout done");
+
// Add commits to staging Area
git.add().addFilepattern(".").call();
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
deleted file mode 100644
index c20698962e6..00000000000
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ /dev/null
@@ -1,383 +0,0 @@
-package org.jabref.gui.autosaveandbackup;
-
-import java.lang.reflect.Field;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.StreamSupport;
-
-import org.jabref.gui.LibraryTab;
-import org.jabref.gui.backup.BackupEntry;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.model.database.BibDatabase;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntryTypesManager;
-
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-import org.mockito.Answers;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-class BackupManagerGitTest {
-
- @TempDir
- Path tempDir;
- Path backupDir;
- Git git;
- CliPreferences preferences;
- BibEntryTypesManager entryTypesManager;
- LibraryTab libraryTab;
- BibDatabaseContext bibDatabaseContext;
-
- @BeforeEach
- void setup() throws Exception {
- backupDir = tempDir.resolve("backup");
- Files.createDirectories(backupDir);
-
- // Initialize the Git repository inside backupDir
- git = Git.init().setDirectory(backupDir.toFile()).call();
-
- // Mock dependencies
- preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
- entryTypesManager = mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS);
- libraryTab = mock(LibraryTab.class);
-
- when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDir);
-
- // Create a test file in the backup directory
- Path testFile = backupDir.resolve("testfile.bib");
- Files.writeString(testFile, "This is a test file.", StandardCharsets.UTF_8);
-
- // Add and commit the test file to the repository
- git.add().addFilepattern("testfile.bib").call();
- git.commit().setMessage("Initial commit").call();
- }
-
- @Test
- void testBackupManagerInitializesGitRepository() throws Exception {
- // Ensure the backup directory exists
- Path backupDir = tempDir.resolve("backup");
- Files.createDirectories(backupDir);
-
- // Create a BibDatabaseContext
- Path databaseFile = tempDir.resolve("test.bib");
- Files.writeString(databaseFile, "Initial content");
- var bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
- bibDatabaseContext.setDatabasePath(databaseFile);
-
- // Initialize BackupManagerGit
- BackupManagerGit manager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
-
- // Ensure the Git repository is initialized in the backup directory
- assertTrue(Files.exists(backupDir.resolve(".git")), "Git repository not initialized in backup directory");
- }
-
- @Test
- void testBackupGitDiffers_NoDifferences() throws Exception {
- // Verify that there is no difference between the file and the last commit
- Path testFile = backupDir.resolve("testfile.bib");
- boolean differs = BackupManagerGit.backupGitDiffers(backupDir);
- assertFalse(differs, "Expected no difference between the file and the last commit");
- }
-
- @Test
- void testBackupGitDiffers_WithDifferences() throws Exception {
- // Modify the test file to create differences
- Path testFile = backupDir.resolve("testfile.bib");
- Files.writeString(testFile, "Modified content", StandardCharsets.UTF_8);
-
- boolean differs = BackupManagerGit.backupGitDiffers(backupDir;
- assertTrue(differs, "Expected differences between the file and the last commit");
- }
-
- @Test
- void testNoNewRepositoryCreated() throws Exception {
- // Create a fake file to simulate the database file
- Path databaseFile = tempDir.resolve("test.bib");
- Files.writeString(databaseFile, "Initial content");
-
- // Set up BibDatabaseContext with the file path
- var bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
- bibDatabaseContext.setDatabasePath(databaseFile);
-
- // Ensure the initial repository is created
- BackupManagerGit initialManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- assertTrue(Files.exists(backupDir.resolve(".git"))); // Ensure the repo exists
-
- // Use backupGitDiffers to check if the backup differs
- boolean createBackup;
- if (bibDatabaseContext.getDatabasePath().isPresent()) {
- createBackup = BackupManagerGit.backupGitDiffers(backupDir);
- } else {
- fail("Database path is not present");
- return; // Avoid further execution if the path is missing
- }
-
- // Shutdown the initial manager
- BackupManagerGit.shutdown(bibDatabaseContext, createBackup, bibDatabaseContext.getDatabasePath().get());
-
- // Create another instance pointing to the same backup directory
- BackupManagerGit newManager = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- assertTrue(Files.exists(backupDir.resolve(".git"))); // Ensure no new repo is created
-
- // Shutdown the new manager
- BackupManagerGit.shutdown(bibDatabaseContext, createBackup, bibDatabaseContext.getDatabasePath().get());
- }
-
- @Test
- void testStartMethod() throws Exception {
- // Arrange: Set up necessary dependencies and mock objects
- Path databaseFile = tempDir.resolve("test.bib");
- Files.writeString(databaseFile, "Initial content");
-
- BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
- bibDatabaseContext.setDatabasePath(databaseFile);
-
- Path backupDirectory = tempDir.resolve("backup");
- Files.createDirectories(backupDirectory);
-
- // Mock preferences to return the backup directory
- when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDirectory);
-
- // Act: Call the start method
- BackupManagerGit backupManager = BackupManagerGit.start(
- libraryTab,
- bibDatabaseContext,
- entryTypesManager,
- preferences
- );
-
- // Assert: Verify the outcomes
- // Ensure a Git repository is initialized in the backup directory
- assertTrue(Files.exists(backupDirectory.resolve(".git")), "Git repository not initialized");
-
- // Use reflection to access the private `runningInstances`
- Field runningInstancesField = BackupManagerGit.class.getDeclaredField("runningInstances");
- runningInstancesField.setAccessible(true);
- @SuppressWarnings("unchecked")
- Set runningInstances = (Set) runningInstancesField.get(null);
-
- // Ensure the backup manager is added to the running instances
- assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
-
- // Clean up by shutting down the backup manager
- BackupManagerGit.shutdown(bibDatabaseContext, false, databaseFile);
- }
-
- @Test
- void testStartBackupTaskWithReflection() throws Exception {
- // Arrange: Similar setup as above
- Path databaseFile = tempDir.resolve("test.bib");
- Files.writeString(databaseFile, "Initial content");
-
- BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
- bibDatabaseContext.setDatabasePath(databaseFile);
-
- Path backupDirectory = tempDir.resolve("backup");
- Files.createDirectories(backupDirectory);
-
- when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDirectory);
-
- BackupManagerGit backupManager = BackupManagerGit.start(
- libraryTab,
- bibDatabaseContext,
- entryTypesManager,
- preferences
- );
-
- // Act: Start the backup task
- // private void startBackupTask(Path backupDir, Path originalPath)
- backupManager.startBackupTask(backupDirectory, bibDatabaseContext.getDatabasePath().orElse());
-
- // Simulate passage of time
- Thread.sleep(100);
-
- // Use reflection to access the private `runningInstances`
- Field runningInstancesField = BackupManagerGit.class.getDeclaredField("runningInstances");
- runningInstancesField.setAccessible(true);
- @SuppressWarnings("unchecked")
- Set runningInstances = (Set) runningInstancesField.get(null);
-
- // Assert: Verify the backup task is active
- assertTrue(runningInstances.contains(backupManager), "Backup manager not added to running instances");
-
- // Clean up
- BackupManagerGit.shutdown(bibDatabaseContext, false, databaseFile);
- }
-
- @Test
- void testRestoreBackup() throws Exception {
- // Create multiple commits
- ObjectId targetCommitId = null;
- for (int i = 1; i <= 3; i++) {
- Path file = backupDir.resolve("file" + i + ".txt");
- Files.writeString(file, "Content of file " + i);
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Commit " + i).call();
- if (i == 2) {
- // Save the ID of the second commit for testing
- targetCommitId = commit.getId();
- }
- }
-
- // Act: Call restoreBackup
- BackupManagerGit.restoreBackup(tempDir.resolve("restored.txt"), backupDir, targetCommitId);
-
- // Assert: Verify the repository has a new commit after restoration
- try (RevWalk revWalk = new RevWalk(git.getRepository())) {
- RevCommit headCommit = revWalk.parseCommit(git.getRepository().resolve("HEAD"));
- assertTrue(
- headCommit.getShortMessage().contains("Restored content from commit: " + targetCommitId.getName()),
- "A new commit should indicate the restoration"
- );
- }
-
- // Assert: Ensure the file from the restored commit exists
- assertTrue(
- Files.exists(backupDir.resolve("file2.txt")),
- "File from the restored commit should be present"
- );
-
- // Assert: Ensure files from later commits still exist
- assertTrue(
- Files.exists(backupDir.resolve("file3.txt")),
- "File from later commits should still exist after restoration"
- );
-
- // Assert: Ensure earlier files still exist
- assertTrue(
- Files.exists(backupDir.resolve("file1.txt")),
- "File from earlier commits should still exist after restoration"
- );
- }
-
- @Test
- void testRetrieveCommits() throws Exception {
- // Create multiple commits in the Git repository
- List commitIds = new ArrayList<>();
- for (int i = 1; i <= 10; i++) {
- Path file = backupDir.resolve("file" + i + ".txt");
- Files.writeString(file, "Content of file " + i);
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Commit " + i).call();
- commitIds.add(commit.getId());
- }
-
- // Act: Call retrieveCommits to get the last 5 commits
- // Arrange: Similar setup as above
- Path databaseFile = tempDir.resolve("test.bib");
- Files.writeString(databaseFile, "Initial content");
-
- BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
- bibDatabaseContext.setDatabasePath(databaseFile);
-
- Path backupDirectory = tempDir.resolve("backup");
- Files.createDirectories(backupDirectory);
-
- when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDirectory);
-
- BackupManagerGit backupManager = BackupManagerGit.start(
- libraryTab,
- bibDatabaseContext,
- entryTypesManager,
- preferences
- );
-
- List retrievedCommits = backupManager.retrieveCommits(backupDir, 5);
-
- // Assert: Verify the number of commits retrieved
- assertEquals(5, retrievedCommits.size(), "Should retrieve the last 5 commits");
-
- // Assert: Verify the content of the retrieved commits
- for (int i = 0; i < 5; i++) {
- RevCommit retrievedCommit = retrievedCommits.get(i);
- int finalI = i;
- RevCommit expectedCommit = StreamSupport.stream(git.log().call().spliterator(), false)
- .filter(commit -> commit.getId().equals(commitIds.get(commitIds.size() - 5 + finalI)))
- .findFirst()
- .orElse(null);
-
- assertNotNull(expectedCommit, "Expected commit should exist in the repository");
- assertEquals(expectedCommit.getFullMessage(), retrievedCommit.getFullMessage(),
- "Commit messages should match");
- assertEquals(expectedCommit.getId(), retrievedCommit.getId(),
- "Commit IDs should match");
- }
- }
-
- @Test
- void testRetrieveCommitDetails() throws Exception {
- // Create multiple commits in the Git repository
- List commits = new ArrayList<>();
- for (int i = 1; i <= 5; i++) {
- Path file = backupDir.resolve("file" + i + ".txt");
- Files.writeString(file, "Content of file " + i);
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Commit " + i).call();
- commits.add(commit);
- }
-
- // Act: Call retrieveCommitDetails to get the details of the commits
- // Arrange: Similar setup as above
- Path databaseFile = tempDir.resolve("test.bib");
- Files.writeString(databaseFile, "Initial content");
-
- BibDatabaseContext bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
- bibDatabaseContext.setDatabasePath(databaseFile);
-
- Path backupDirectory = tempDir.resolve("backup");
- Files.createDirectories(backupDirectory);
-
- when(preferences.getFilePreferences().getBackupDirectory()).thenReturn(backupDirectory);
-
- BackupManagerGit backupManager = BackupManagerGit.start(
- libraryTab,
- bibDatabaseContext,
- entryTypesManager,
- preferences
- );
- List commitDetails = BackupManagerGit.retrieveCommitDetails(commits, backupDir);
-
- // Assert: Verify the number of commits
- assertEquals(5, commitDetails.size(), "Should retrieve details for 5 commits");
-
- // Assert: Verify the content of the retrieved commit details
- for (int i = 0; i < 5; i++) {
- List commitInfo = (List) commitDetails.get(i);
- RevCommit commit = commits.get(i);
-
- // Verify commit ID
- assertEquals(commit.getName(), commitInfo.get(0), "Commit ID should match");
-
- // Verify commit size (this is a bit tricky, so just check it's a valid size string)
- String sizeFormatted = commitInfo.get(1);
- assertTrue(sizeFormatted.contains("Ko") || sizeFormatted.contains("Mo"), "Commit size should be properly formatted");
-
- // Verify commit date
- String commitDate = commitInfo.get(2);
- assertTrue(commitDate.contains(commit.getAuthorIdent().getWhen().toString()), "Commit date should match");
- }
- }
-}
-
-
-
-
-
-
-
From c219a98cd3e11b53f29789cf283551f2a802ee5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Mon, 2 Dec 2024 01:20:35 +0100
Subject: [PATCH 53/84] correct comment
---
src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index e881631bda5..375459d688d 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -268,7 +268,7 @@ private boolean save(Path targetPath, SaveDatabaseMode mode) {
}
private boolean saveDatabase(Path file, boolean selectedOnly, Charset encoding, BibDatabaseWriter.SaveType saveType, SelfContainedSaveOrder saveOrder) throws SaveException {
- // if this code is adapted, please also adapt org.jabref.logic.autosaveandbackup.BackupManagerGit.performBackup
+ // if this code is adapted, please also adapt org.jabref.logic.autosaveandbackup.BackupManager.performBackup
SelfContainedSaveConfiguration saveConfiguration
= new SelfContainedSaveConfiguration(saveOrder, false, saveType, preferences.getLibraryPreferences().shouldAlwaysReformatOnSave());
BibDatabaseContext bibDatabaseContext = libraryTab.getBibDatabaseContext();
From 5ae740735c13c3fc8b78e9019b2523afbcd25bc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Mon, 2 Dec 2024 21:40:57 +0100
Subject: [PATCH 54/84] Resolve the comment : Remove this here, we already have
a newer version of jgit some lines down
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 528c5fded26..70befe38f8c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -156,7 +156,7 @@ jacoco {
dependencies {
// Include all jar-files in the 'lib' folder as dependencies
implementation fileTree(dir: 'lib', includes: ['*.jar'])
- implementation 'org.eclipse.jgit:org.eclipse.jgit:6.7.0.202309050840-r'
+
def pdfbox = "3.0.3"
implementation ("org.apache.pdfbox:pdfbox:$pdfbox") {
exclude group: 'commons-logging'
From ebe6f6b7a03e35a91b7836a62883cc6c06ab8588 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Mon, 2 Dec 2024 21:46:31 +0100
Subject: [PATCH 55/84] Resolve the comment : delete this file
(module-info.java), it's not needed
---
src/jmh/java/module-info.java | 8 --------
1 file changed, 8 deletions(-)
delete mode 100644 src/jmh/java/module-info.java
diff --git a/src/jmh/java/module-info.java b/src/jmh/java/module-info.java
deleted file mode 100644
index 4c0962156dc..00000000000
--- a/src/jmh/java/module-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- *
- */
-/**
- *
- */
-module JabRef {
-}
\ No newline at end of file
From b0cc7f7b6ed8fd9c8a64ccdc089387d7306a0733 Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Wed, 4 Dec 2024 00:06:30 +0100
Subject: [PATCH 56/84] Adding BackupManagerGitTest class (with 5/5 passed
tests)
---
.../autosaveandbackup/BackupManagerGit.java | 47 +++++-
.../BackupManagerGitTest.java | 157 ++++++++++++++++++
.../autosaveandbackup/BackupManagerTest.java | 1 -
.../BackupManagerTestJGit.java | 4 -
4 files changed, 196 insertions(+), 13 deletions(-)
create mode 100644 src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
delete mode 100644 src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTestJGit.java
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 89f8cefc246..54ae9f59c04 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -17,6 +17,7 @@
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jabref.gui.LibraryTab;
@@ -46,9 +47,9 @@ public class BackupManagerGit {
private static final Logger LOGGER = LoggerFactory.getLogger(BackupManagerGit.class);
- private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
+ private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 4;
- private static Set runningInstances = new HashSet();
+ static Set runningInstances = new HashSet();
private static Git git;
@@ -62,6 +63,14 @@ public class BackupManagerGit {
private boolean needsBackup = false;
BackupManagerGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ // Get the path of the BibDatabase file
+ Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
+
+ if (!Files.exists(dbFile)) {
+ LOGGER.error("Database file does not exist: {}", dbFile);
+ throw new IOException("Database file not found: " + dbFile);
+ }
+
this.bibDatabaseContext = bibDatabaseContext;
LOGGER.info("Backup manager initialized for file: {}", bibDatabaseContext.getDatabasePath().orElseThrow());
this.entryTypesManager = entryTypesManager;
@@ -86,7 +95,7 @@ public class BackupManagerGit {
}
// Get the path of the BibDatabase file
- Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
+ Path dbFilePath = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
// Copy the database file to the backup directory
Path backupFilePath = backupDirPath.resolve(dbFile.getFileName());
@@ -99,7 +108,24 @@ public class BackupManagerGit {
}
}
- private static void ensureGitInitialized(Path backupDir) throws IOException, GitAPIException {
+ private static String normalizeBibTeX(String input) {
+ if (input == null || input.isBlank()) {
+ return "";
+ }
+
+ // Diviser les lignes et traiter chaque ligne
+ Stream lines = input.lines();
+
+ // Normalisation des lignes
+ String normalized = lines
+ .map(String::trim) // Supprimer les espaces en début et fin de ligne
+ .filter(line -> !line.isBlank()) // Supprimer les lignes vides
+ .collect(Collectors.joining("\n")); // Réassembler avec des sauts de ligne
+
+ return normalized;
+ }
+
+ static void ensureGitInitialized(Path backupDir) throws IOException, GitAPIException {
// This method was created because the initialization of the Git object, when written in the constructor, was causing a NullPointerException
// because the first method called when loading the database is BackupGitdiffers
@@ -140,6 +166,7 @@ private static void ensureGitInitialized(Path backupDir) throws IOException, Git
*/
public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ LOGGER.info("In methode Start");
BackupManagerGit backupManagerGit = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), bibDatabaseContext);
runningInstances.add(backupManagerGit);
@@ -170,7 +197,7 @@ public static void shutdown(BibDatabaseContext bibDatabaseContext, boolean creat
*/
void startBackupTask(Path backupDir, BibDatabaseContext bibDatabaseContext) {
- LOGGER.info("Initializing backup task for directory: {} and file: {}", backupDir);
+ LOGGER.info("Initializing backup task for directory: {} and file: {}", backupDir, bibDatabaseContext.getDatabasePath().orElseThrow());
executor.scheduleAtFixedRate(
() -> {
try {
@@ -229,7 +256,8 @@ protected void performBackup(Path backupDir) throws IOException, GitAPIException
RevCommit commit = git.commit()
.setMessage("Backup at " + Instant.now().toString())
.call();
- LOGGER.info("Backup committed in :" + backupDir + " with commit ID: " + commit.getName());
+ LOGGER.info("Backup committed in :" + backupDir + " with commit ID: " + commit.getName()
+ + " for the file : {}", bibDatabaseContext.getDatabasePath().orElseThrow());
}
public synchronized void listen(BibDatabaseContextChangedEvent event) {
@@ -309,6 +337,7 @@ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAP
LOGGER.info("Checking file: {}", relativePath);
try {
+
// Check if the file exists in the latest commit
ObjectId objectId = repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/"));
if (objectId == null) {
@@ -318,8 +347,10 @@ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAP
// Compare the content of the file in the Git repository with the current file
ObjectLoader loader = repository.open(objectId);
- String committedContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
- String currentContent = Files.readString(path, StandardCharsets.UTF_8);
+ String committedContent = normalizeBibTeX(new String(loader.getBytes(), StandardCharsets.UTF_8));
+ String currentContent = normalizeBibTeX(Files.readString(path, StandardCharsets.UTF_8));
+ LOGGER.info("Committed content: {}", committedContent);
+ LOGGER.info("Current content: {}", currentContent);
// If the contents differ, return true
if (!currentContent.equals(committedContent)) {
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
new file mode 100644
index 00000000000..9f5792634a4
--- /dev/null
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -0,0 +1,157 @@
+
+package org.jabref.gui.autosaveandbackup;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.jabref.gui.LibraryTab;
+import org.jabref.logic.FilePreferences;
+import org.jabref.logic.preferences.CliPreferences;
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.metadata.MetaData;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BackupManagerGitTest {
+
+ private Path tempDir1;
+ private Path tempDir2;
+ private Path tempDir;
+ private LibraryTab mockLibraryTab;
+ private BibDatabaseContext mockDatabaseContext1;
+ private BibDatabaseContext mockDatabaseContext2;
+ private BibEntryTypesManager mockEntryTypesManager;
+ private CliPreferences mockPreferences;
+ private Path mockDatabasePath1;
+ private Path mockDatabasePath2;
+
+ @BeforeEach
+ public void setUp(@TempDir Path tempDir) throws IOException, GitAPIException {
+ mockLibraryTab = mock(LibraryTab.class);
+ mockDatabaseContext1 = mock(BibDatabaseContext.class);
+ mockDatabaseContext2 = mock(BibDatabaseContext.class);
+ mockEntryTypesManager = mock(BibEntryTypesManager.class);
+ mockPreferences = mock(CliPreferences.class);
+
+ FilePreferences filePreferences = mock(FilePreferences.class);
+ when(mockPreferences.getFilePreferences()).thenReturn(filePreferences);
+
+ // Create temporary backup directories
+
+ this.tempDir = tempDir.resolve("");
+ this.tempDir1 = tempDir.resolve("backup1");
+ this.tempDir2 = tempDir.resolve("backup2");
+ Files.createDirectories(this.tempDir); // Ensure the directory exists
+ Files.createDirectories(this.tempDir1); // Ensure the directory exists
+ Files.createDirectories(this.tempDir2); // Ensure the directory exists
+
+ // Mock the database paths and create the actual files
+ mockDatabasePath1 = tempDir1.resolve("test1.bib");
+ mockDatabasePath2 = tempDir2.resolve("test2.bib");
+
+ Files.writeString(mockDatabasePath1, "Mock content for testing 1"); // Create the file
+ Files.writeString(mockDatabasePath2, "Mock content for testing 2"); // Create the file
+
+ when(mockDatabaseContext1.getDatabasePath()).thenReturn(java.util.Optional.of(mockDatabasePath1));
+ when(mockDatabaseContext2.getDatabasePath()).thenReturn(java.util.Optional.of(mockDatabasePath2));
+
+ when(filePreferences.getBackupDirectory()).thenReturn(tempDir);
+
+ // Mock BibDatabase for all contexts
+ BibDatabase mockDatabase1 = mock(BibDatabase.class);
+ when(mockDatabaseContext1.getDatabase()).thenReturn(mockDatabase1);
+
+ BibDatabase mockDatabase2 = mock(BibDatabase.class);
+ when(mockDatabaseContext2.getDatabase()).thenReturn(mockDatabase2);
+
+ // Mock MetaData for all contexts (if needed elsewhere)
+ MetaData mockMetaData1 = mock(MetaData.class);
+ when(mockDatabaseContext1.getMetaData()).thenReturn(mockMetaData1);
+
+ MetaData mockMetaData2 = mock(MetaData.class);
+ when(mockDatabaseContext2.getMetaData()).thenReturn(mockMetaData2);
+ }
+
+ @AfterEach
+ void tearDown() throws IOException {
+ // Delete the temporary directory
+ Files.walk(tempDir)
+ .map(Path::toFile)
+ .forEach(file -> {
+ if (!file.delete()) {
+ file.deleteOnExit();
+ }
+ });
+ }
+
+ @Test
+ void testInitializationCreatesBackupDirectory() throws IOException, GitAPIException {
+ // Create BackupManagerGit
+ BackupManagerGit manager1 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, mockPreferences);
+ BackupManagerGit manager2 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext2, mockEntryTypesManager, mockPreferences);
+ // Check if the backup directory exists
+ assertTrue(Files.exists(tempDir), " directory should be created wich contains .git and single copies og .bib");
+ assertTrue(Files.exists(tempDir1), "Backup directory should be created during initialization.");
+ assertTrue(Files.exists(tempDir2), "Backup directory should be created during initialization.");
+ }
+
+ @Test
+ void testGitInitialization() throws IOException, GitAPIException {
+ // Initialize Git
+ BackupManagerGit.ensureGitInitialized(tempDir);
+
+ // Verify that the .git directory is created
+ Path gitDir = tempDir.resolve(".git");
+ assertTrue(Files.exists(gitDir), ".git directory should be created during Git initialization.");
+ }
+
+ @Test
+ void testBackupFileCopiedToDirectory() throws IOException, GitAPIException {
+
+ BackupManagerGit manager1 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, mockPreferences);
+ BackupManagerGit manager2 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext2, mockEntryTypesManager, mockPreferences);
+
+ // Verify the file is copied to the backup directory
+ Path backupFile1 = tempDir.resolve(this.mockDatabasePath1.getFileName());
+ Path backupFile2 = tempDir.resolve(this.mockDatabasePath2.getFileName());
+ assertTrue(Files.exists(backupFile1), "Database file should be copied to the backup directory.");
+ }
+
+ @Test
+ public void testStart() throws IOException, GitAPIException {
+ BackupManagerGit startedManager = BackupManagerGit.start(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, mockPreferences);
+ assertNotNull(startedManager);
+ }
+
+ @Test
+ void testPerformBackupCommitsChanges() throws IOException, GitAPIException {
+ // Initialize Git
+ BackupManagerGit.ensureGitInitialized(tempDir);
+
+ // Create a test file
+ Path dbFile1 = tempDir.resolve("test1.bib");
+ Files.writeString(dbFile1, "Initial content of test 1");
+
+ // Create BackupManagerGit and perform backup
+ BackupManagerGit manager = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, mockPreferences);
+ manager.performBackup(tempDir);
+
+ // Verify that changes are committed
+ try (Git git = Git.open(tempDir.toFile())) {
+ assertTrue(git.status().call().isClean(), "Git repository should have no uncommitted changes after backup.");
+ }
+ }
+}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
index a45d7cbb9c1..3233cf47a13 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
@@ -20,7 +20,6 @@
import org.jabref.model.groups.event.GroupUpdatedEvent;
import org.jabref.model.metadata.MetaData;
import org.jabref.model.metadata.event.MetaDataChangedEvent;
-
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTestJGit.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTestJGit.java
deleted file mode 100644
index 971a51f8a73..00000000000
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTestJGit.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.jabref.gui.autosaveandbackup;
-
-public class BackupManagerTestJGit {
-}
From 7085fc49373cf8d64e29c6bebf695e312f05222a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 4 Dec 2024 13:19:50 +0100
Subject: [PATCH 57/84] - Implement the unique file names in order to avoid
conflicts when copying two files with the same name in the git repo - delete
BackupManager.java, BackupManagerDiscardedTest.java, BackupManagerTest.java,
the project building is successful
---
src/main/java/org/jabref/gui/LibraryTab.java | 3 +-
.../gui/autosaveandbackup/BackupManager.java | 396 ------------------
.../autosaveandbackup/BackupManagerGit.java | 110 +++--
.../gui/exporter/SaveDatabaseAction.java | 2 +-
.../BackupManagerDiscardedTest.java | 109 -----
.../autosaveandbackup/BackupManagerTest.java | 190 ---------
6 files changed, 73 insertions(+), 737 deletions(-)
delete mode 100644 src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
delete mode 100644 src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerDiscardedTest.java
delete mode 100644 src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index 181cc9fa5bb..b27c7c70f33 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -803,8 +803,7 @@ private void onClosed(Event event) {
}
try {
BackupManagerGit.shutdown(bibDatabaseContext,
- preferences.getFilePreferences().shouldCreateBackup(),
- preferences.getFilePreferences().getBackupDirectory());
+ preferences.getFilePreferences().shouldCreateBackup());
} catch (RuntimeException e) {
LOGGER.error("Problem when shutting down backup manager", e);
}
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
deleted file mode 100644
index e6458520653..00000000000
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java
+++ /dev/null
@@ -1,396 +0,0 @@
-package org.jabref.gui.autosaveandbackup;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.FileTime;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-import javafx.scene.control.TableColumn;
-
-import org.jabref.gui.LibraryTab;
-import org.jabref.gui.maintable.BibEntryTableViewModel;
-import org.jabref.gui.maintable.columns.MainTableColumn;
-import org.jabref.logic.bibtex.InvalidFieldValueException;
-import org.jabref.logic.exporter.AtomicFileWriter;
-import org.jabref.logic.exporter.BibWriter;
-import org.jabref.logic.exporter.BibtexDatabaseWriter;
-import org.jabref.logic.exporter.SelfContainedSaveConfiguration;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.util.BackupFileType;
-import org.jabref.logic.util.CoarseChangeFilter;
-import org.jabref.logic.util.io.BackupFileUtil;
-import org.jabref.model.database.BibDatabase;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.database.event.BibDatabaseContextChangedEvent;
-import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.BibEntryTypesManager;
-import org.jabref.model.metadata.SaveOrder;
-import org.jabref.model.metadata.SelfContainedSaveOrder;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Backups the given bib database file from {@link BibDatabaseContext} on every {@link BibDatabaseContextChangedEvent}.
- * An intelligent {@link ExecutorService} with a {@link BlockingQueue} prevents a high load while making backups and
- * rejects all redundant backup tasks. This class does not manage the .bak file which is created when opening a
- * database.
- */
-
-public class BackupManager {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(BackupManager.class);
-
- private static final int MAXIMUM_BACKUP_FILE_COUNT = 10;
-
- private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
-
- private static Set runningInstances = new HashSet<>();
-
- private final BibDatabaseContext bibDatabaseContext;
- private final CliPreferences preferences;
- private final ScheduledThreadPoolExecutor executor;
- private final CoarseChangeFilter changeFilter;
- private final BibEntryTypesManager entryTypesManager;
- private final LibraryTab libraryTab;
-
- // Contains a list of all backup paths
- // During writing, the less recent backup file is deleted
- private final Queue backupFilesQueue = new LinkedBlockingQueue<>();
- private boolean needsBackup = false;
-
- BackupManager(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
- this.bibDatabaseContext = bibDatabaseContext;
- this.entryTypesManager = entryTypesManager;
- this.preferences = preferences;
- this.executor = new ScheduledThreadPoolExecutor(2);
- this.libraryTab = libraryTab;
-
- changeFilter = new CoarseChangeFilter(bibDatabaseContext);
- changeFilter.registerListener(this);
- }
-
- /**
- * Determines the most recent backup file name
- */
- static Path getBackupPathForNewBackup(Path originalPath, Path backupDir) {
- return BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(originalPath, BackupFileType.BACKUP, backupDir);
- }
-
- /**
- * Determines the most recent existing backup file name
- */
- static Optional getLatestBackupPath(Path originalPath, Path backupDir) {
- return BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
- }
-
- /**
- * Starts the BackupManager which is associated with the given {@link BibDatabaseContext}. As long as no database
- * file is present in {@link BibDatabaseContext}, the {@link BackupManager} will do nothing.
- *
- * This method is not thread-safe. The caller has to ensure that this method is not called in parallel.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- */
- public static BackupManager start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) {
- BackupManager BackupManager = new BackupManager(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
- BackupManager.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
- runningInstances.add(BackupManager);
- return BackupManager;
- }
-
- /**
- * Marks the backup as discarded at the library which is associated with the given {@link BibDatabaseContext}.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- */
- public static void discardBackup(BibDatabaseContext bibDatabaseContext, Path backupDir) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(BackupManager -> BackupManager.discardBackup(backupDir));
- }
-
- /**
- * Shuts down the BackupManager which is associated with the given {@link BibDatabaseContext}.
- *
- * @param bibDatabaseContext Associated {@link BibDatabaseContext}
- * @param createBackup True, if a backup should be created
- * @param backupDir The path to the backup directory
- */
- public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(BackupManager -> BackupManager.shutdown(backupDir, createBackup));
- runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
- }
-
- /**
- * Checks whether a backup file exists for the given database file. If it exists, it is checked whether it is
- * newer and different from the original.
- *
- * In case a discarded file is present, the method also returns false
, See also {@link #discardBackup(Path)}.
- *
- * @param originalPath Path to the file a backup should be checked for. Example: jabref.bib.
- *
- * @return true
if backup file exists AND differs from originalPath. false
is the
- * "default" return value in the good case. In case a discarded file exists, false
is returned, too.
- * In the case of an exception true
is returned to ensure that the user checks the output.
- */
- public static boolean backupFileDiffers(Path originalPath, Path backupDir) {
- Path discardedFile = determineDiscardedFile(originalPath, backupDir);
- if (Files.exists(discardedFile)) {
- try {
- Files.delete(discardedFile);
- } catch (IOException e) {
- LOGGER.error("Could not remove discarded file {}", discardedFile, e);
- return true;
- }
- return false;
- }
- return getLatestBackupPath(originalPath, backupDir).map(latestBackupPath -> {
- FileTime latestBackupFileLastModifiedTime;
- try {
- latestBackupFileLastModifiedTime = Files.getLastModifiedTime(latestBackupPath);
- } catch (IOException e) {
- LOGGER.debug("Could not get timestamp of backup file {}", latestBackupPath, e);
- // If we cannot get the timestamp, we do show any warning
- return false;
- }
- FileTime currentFileLastModifiedTime;
- try {
- currentFileLastModifiedTime = Files.getLastModifiedTime(originalPath);
- } catch (IOException e) {
- LOGGER.debug("Could not get timestamp of current file file {}", originalPath, e);
- // If we cannot get the timestamp, we do show any warning
- return false;
- }
- if (latestBackupFileLastModifiedTime.compareTo(currentFileLastModifiedTime) <= 0) {
- // Backup is older than current file
- // We treat the backup as non-different (even if it could differ)
- return false;
- }
- try {
- boolean result = Files.mismatch(originalPath, latestBackupPath) != -1L;
- if (result) {
- LOGGER.info("Backup file {} differs from current file {}", latestBackupPath, originalPath);
- }
- return result;
- } catch (IOException e) {
- LOGGER.debug("Could not compare original file and backup file.", e);
- // User has to investigate in this case
- return true;
- }
- }).orElse(false);
- }
-
- /**
- * Restores the backup file by copying and overwriting the original one.
- *
- * @param originalPath Path to the file which should be equalized to the backup file.
- */
- public static void restoreBackup(Path originalPath, Path backupDir) {
- Optional backupPath = getLatestBackupPath(originalPath, backupDir);
- if (backupPath.isEmpty()) {
- LOGGER.error("There is no backup file");
- return;
- }
- try {
- Files.copy(backupPath.get(), originalPath, StandardCopyOption.REPLACE_EXISTING);
- } catch (IOException e) {
- LOGGER.error("Error while restoring the backup file.", e);
- }
- }
-
- Optional determineBackupPathForNewBackup(Path backupDir) {
- return bibDatabaseContext.getDatabasePath().map(path -> BackupManager.getBackupPathForNewBackup(path, backupDir));
- }
-
- /**
- * This method is called as soon as the scheduler says: "Do the backup"
- *
- * SIDE EFFECT: Deletes oldest backup file
- *
- * @param backupPath the full path to the file where the library should be backed up to
- */
- void performBackup(Path backupPath) {
- if (!needsBackup) {
- return;
- }
-
- // We opted for "while" to delete backups in case there are more than 10
- while (backupFilesQueue.size() >= MAXIMUM_BACKUP_FILE_COUNT) {
- Path oldestBackupFile = backupFilesQueue.poll();
- try {
- Files.delete(oldestBackupFile);
- } catch (IOException e) {
- LOGGER.error("Could not delete backup file {}", oldestBackupFile, e);
- }
- }
-
- // l'ordre dans lequel les entrées BibTeX doivent être écrites dans le fichier de sauvegarde.
- // Si l'utilisateur a trié la table d'affichage des entrées dans JabRef, cet ordre est récupéré.
- // Sinon, un ordre par défaut est utilisé.
- // code similar to org.jabref.gui.exporter.SaveDatabaseAction.saveDatabase
- SelfContainedSaveOrder saveOrder = bibDatabaseContext
- .getMetaData().getSaveOrder()
- .map(so -> {
- if (so.getOrderType() == SaveOrder.OrderType.TABLE) {
- // We need to "flatten out" SaveOrder.OrderType.TABLE as BibWriter does not have access to preferences
- List> sortOrder = libraryTab.getMainTable().getSortOrder();
- return new SelfContainedSaveOrder(
- SaveOrder.OrderType.SPECIFIED,
- sortOrder.stream()
- .filter(col -> col instanceof MainTableColumn>)
- .map(column -> ((MainTableColumn>) column).getModel())
- .flatMap(model -> model.getSortCriteria().stream())
- .toList());
- } else {
- return SelfContainedSaveOrder.of(so);
- }
- })
- .orElse(SaveOrder.getDefaultSaveOrder());
-
- // Elle configure la sauvegarde, en indiquant qu'aucune sauvegarde supplémentaire (backup) ne doit être créée,
- // que l'ordre de sauvegarde doit être celui défini, et que les entrées doivent être formatées selon les préférences
- // utilisateur.
- SelfContainedSaveConfiguration saveConfiguration = (SelfContainedSaveConfiguration) new SelfContainedSaveConfiguration()
- .withMakeBackup(false)
- .withSaveOrder(saveOrder)
- .withReformatOnSave(preferences.getLibraryPreferences().shouldAlwaysReformatOnSave());
-
- // "Clone" the database context
- // We "know" that "only" the BibEntries might be changed during writing (see [org.jabref.logic.exporter.BibDatabaseWriter.savePartOfDatabase])
- // Chaque entrée BibTeX (comme un article, livre, etc.) est clonée en utilisant la méthode clone().
- // Cela garantit que les modifications faites pendant la sauvegarde n'affecteront pas l'entrée originale.
- List list = bibDatabaseContext.getDatabase().getEntries().stream()
- .map(BibEntry::clone)
- .map(BibEntry.class::cast)
- .toList();
- BibDatabase bibDatabaseClone = new BibDatabase(list);
- BibDatabaseContext bibDatabaseContextClone = new BibDatabaseContext(bibDatabaseClone, bibDatabaseContext.getMetaData());
- // Elle définit l'encodage à utiliser pour écrire le fichier. Cela garantit que les caractères spéciaux sont bien sauvegardés.
- Charset encoding = bibDatabaseContext.getMetaData().getEncoding().orElse(StandardCharsets.UTF_8);
- // We want to have successful backups only
- // Thus, we do not use a plain "FileWriter", but the "AtomicFileWriter"
- // Example: What happens if one hard powers off the machine (or kills the jabref process) during writing of the backup?
- // This MUST NOT create a broken backup file that then jabref wants to "restore" from?
- try (Writer writer = new AtomicFileWriter(backupPath, encoding, false)) {
- BibWriter bibWriter = new BibWriter(writer, bibDatabaseContext.getDatabase().getNewLineSeparator());
- new BibtexDatabaseWriter(
- bibWriter,
- saveConfiguration,
- preferences.getFieldPreferences(),
- preferences.getCitationKeyPatternPreferences(),
- entryTypesManager)
- // we save the clone to prevent the original database (and thus the UI) from being changed
- .saveDatabase(bibDatabaseContextClone);
- backupFilesQueue.add(backupPath);
-
- // We wrote the file successfully
- // Thus, we currently do not need any new backup
- this.needsBackup = false;
- } catch (IOException e) {
- logIfCritical(backupPath, e);
- }
- }
-
- private static Path determineDiscardedFile(Path file, Path backupDir) {
- return backupDir.resolve(BackupFileUtil.getUniqueFilePrefix(file) + "--" + file.getFileName() + "--discarded");
- }
-
- /**
- * Marks the backups as discarded.
- *
- * We do not delete any files, because the user might want to recover old backup files.
- * Therefore, we mark discarded backups by a --discarded file.
- */
- public void discardBackup(Path backupDir) {
- Path path = determineDiscardedFile(bibDatabaseContext.getDatabasePath().get(), backupDir);
- try {
- Files.createFile(path);
- } catch (IOException e) {
- LOGGER.info("Could not create backup file {}", path, e);
- }
- }
-
- private void logIfCritical(Path backupPath, IOException e) {
- Throwable innermostCause = e;
- while (innermostCause.getCause() != null) {
- innermostCause = innermostCause.getCause();
- }
- boolean isErrorInField = innermostCause instanceof InvalidFieldValueException;
-
- // do not print errors in field values into the log during autosave
- if (!isErrorInField) {
- LOGGER.error("Error while saving to file {}", backupPath, e);
- }
- }
-
- public synchronized void listen(BibDatabaseContextChangedEvent event) {
- if (!event.isFilteredOut()) {
- this.needsBackup = true;
- }
- }
-
- private void startBackupTask(Path backupDir) {
- fillQueue(backupDir);
- // remplie backupFilesQueue les files .sav de le meme bibl
-
- executor.scheduleAtFixedRate(
- // We need to determine the backup path on each action, because we use the timestamp in the filename
- () -> determineBackupPathForNewBackup(backupDir).ifPresent(path -> this.performBackup(path)),
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- TimeUnit.SECONDS);
- }
- // La méthode fillQueue(backupDir) est définie dans le code et son rôle est de lister et d'ajouter
- // les fichiers de sauvegarde existants dans une file d'attente,
-
- private void fillQueue(Path backupDir) {
- if (!Files.exists(backupDir)) {
- return;
- }
- bibDatabaseContext.getDatabasePath().ifPresent(databasePath -> {
- // code similar to {@link org.jabref.logic.util.io.BackupFileUtil.getPathOfLatestExisingBackupFile}
- final String prefix = BackupFileUtil.getUniqueFilePrefix(databasePath) + "--" + databasePath.getFileName();
- try {
- List allSavFiles = Files.list(backupDir)
- // just list the .sav belonging to the given targetFile
- .filter(p -> p.getFileName().toString().startsWith(prefix))
- // tous les files .sav commencerait par ce prefix
- .sorted().toList();
- backupFilesQueue.addAll(allSavFiles);
- } catch (IOException e) {
- LOGGER.error("Could not determine most recent file", e);
- }
- });
- }
-
- /**
- * Unregisters the BackupManager from the eventBus of {@link BibDatabaseContext}.
- * This method should only be used when closing a database/JabRef in a normal way.
- *
- * @param backupDir The backup directory
- * @param createBackup If the backup manager should still perform a backup
- */
- private void shutdown(Path backupDir, boolean createBackup) {
- changeFilter.unregisterListener(this);
- changeFilter.shutdown();
- executor.shutdown();
-
- if (createBackup) {
- // Ensure that backup is a recent one
- determineBackupPathForNewBackup(backupDir).ifPresent(this::performBackup);
- }
- }
-}
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 54ae9f59c04..44ecb6f71bc 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -15,6 +15,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -45,11 +46,11 @@
public class BackupManagerGit {
- private static final Logger LOGGER = LoggerFactory.getLogger(BackupManagerGit.class);
+ static Set runningInstances = new HashSet();
- private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 4;
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackupManagerGit.class);
- static Set runningInstances = new HashSet();
+ private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
private static Git git;
@@ -63,9 +64,7 @@ public class BackupManagerGit {
private boolean needsBackup = false;
BackupManagerGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
- // Get the path of the BibDatabase file
Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
-
if (!Files.exists(dbFile)) {
LOGGER.error("Database file does not exist: {}", dbFile);
throw new IOException("Database file not found: " + dbFile);
@@ -81,11 +80,9 @@ public class BackupManagerGit {
changeFilter = new CoarseChangeFilter(bibDatabaseContext);
changeFilter.registerListener(this);
- // Ensure the backup directory exists
Path backupDirPath = preferences.getFilePreferences().getBackupDirectory();
LOGGER.info("Backup directory path: {}", backupDirPath);
- // Ensure Git is initialized
ensureGitInitialized(backupDirPath);
File backupDir = backupDirPath.toFile();
@@ -94,20 +91,54 @@ public class BackupManagerGit {
throw new IOException("Unable to create backup directory: " + backupDir);
}
- // Get the path of the BibDatabase file
- Path dbFilePath = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
+ copyDatabaseFileToBackupDir(dbFile, backupDirPath);
+ }
- // Copy the database file to the backup directory
- Path backupFilePath = backupDirPath.resolve(dbFile.getFileName());
- try {
- Files.copy(dbFile, backupFilePath, StandardCopyOption.REPLACE_EXISTING);
- LOGGER.info("Database file copied to backup directory: {}", backupFilePath);
- } catch (IOException e) {
- LOGGER.error("Failed to copy database file to backup directory", e);
- throw new IOException("Error copying database file to backup directory", e);
+ /**
+ * Appends a UUID to a file name, keeping the original extension.
+ *
+ * @param originalFileName The original file name (e.g., library.bib).
+ * @param uuid The UUID to append.
+ * @return The modified file name with the UUID (e.g., library_123e4567-e89b-12d3-a456-426614174000.bib).
+ */
+ private String appendUuidToFileName(String originalFileName, String uuid) {
+ int dotIndex = originalFileName.lastIndexOf('.');
+ if (dotIndex == -1) {
+ // If there's no extension, just append the UUID
+ return originalFileName + "_" + uuid;
}
+
+ // Insert the UUID before the extension
+ String baseName = originalFileName.substring(0, dotIndex);
+ String extension = originalFileName.substring(dotIndex);
+ return baseName + "_" + uuid + extension;
}
+ /**
+ * Retrieves or generates a persistent unique identifier (UUID) for the given file.
+ * The UUID is stored in an extended attribute or a metadata file alongside the original file.
+ *
+ * @param filePath The path to the file.
+ * @return The UUID associated with the file.
+ * @throws IOException If an error occurs while accessing or creating the UUID.
+ */
+ private String getOrGenerateFileUuid(Path filePath) throws IOException {
+ // Define a hidden metadata file to store the UUID
+ Path metadataFile = filePath.resolveSibling("." + filePath.getFileName().toString() + ".uuid");
+
+ // If the UUID metadata file exists, read it
+ if (Files.exists(metadataFile)) {
+ return Files.readString(metadataFile).trim();
+ }
+
+ // Otherwise, generate a new UUID and save it
+ String uuid = UUID.randomUUID().toString();
+ Files.writeString(metadataFile, uuid);
+ LOGGER.info("Generated new UUID for file {}: {}", filePath, uuid);
+ return uuid;
+ }
+
+ // Helper method to normalize BibTeX content
private static String normalizeBibTeX(String input) {
if (input == null || input.isBlank()) {
return "";
@@ -125,6 +156,7 @@ private static String normalizeBibTeX(String input) {
return normalized;
}
+ // Helper method to ensure the Git repository is initialized
static void ensureGitInitialized(Path backupDir) throws IOException, GitAPIException {
// This method was created because the initialization of the Git object, when written in the constructor, was causing a NullPointerException
@@ -153,6 +185,19 @@ static void ensureGitInitialized(Path backupDir) throws IOException, GitAPIExcep
git = new Git(repository);
}
+ // Helper method to copy the database file to the backup directory
+ private void copyDatabaseFileToBackupDir(Path dbFile, Path backupDirPath) throws IOException {
+ String fileUuid = getOrGenerateFileUuid(dbFile);
+ String uniqueFileName = appendUuidToFileName(dbFile.getFileName().toString(), fileUuid);
+ Path backupFilePath = backupDirPath.resolve(uniqueFileName);
+ if (!Files.exists(backupFilePath) || Files.mismatch(dbFile, backupFilePath) != -1) {
+ Files.copy(dbFile, backupFilePath, StandardCopyOption.REPLACE_EXISTING);
+ LOGGER.info("Database file uniquely copied to backup directory: {}", backupFilePath);
+ } else {
+ LOGGER.info("No changes detected; skipping backup for file: {}", uniqueFileName);
+ }
+ }
+
/**
* Starts a new BackupManagerGit instance and begins the backup task.
*
@@ -178,16 +223,15 @@ public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext b
*
* @param bibDatabaseContext the BibDatabaseContext
* @param createBackup whether to create a backup before shutting down
- * @param originalPath the original path of the file to be backed up
*/
- public static void shutdown(BibDatabaseContext bibDatabaseContext, boolean createBackup, Path originalPath) {
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, boolean createBackup) {
runningInstances.stream()
.filter(instance -> instance.bibDatabaseContext == bibDatabaseContext)
- .forEach(backupManager -> backupManager.shutdownGit(bibDatabaseContext.getDatabasePath().orElseThrow().getParent().resolve("backup"), createBackup, originalPath));
+ .forEach(backupManager -> backupManager.shutdownGit(bibDatabaseContext.getDatabasePath().orElseThrow().getParent().resolve("backup"), createBackup));
// Remove the instances associated with the BibDatabaseContext after shutdown
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
- LOGGER.info("Shut down backup manager for file: {}", originalPath);
+ LOGGER.info("Shut down backup manager for file: {}");
}
/**
@@ -201,19 +245,8 @@ void startBackupTask(Path backupDir, BibDatabaseContext bibDatabaseContext) {
executor.scheduleAtFixedRate(
() -> {
try {
- // Copy the database file to the backup directory
Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
-
- // Copy the database file to the backup directory (overwriting any existing file)
- Path backupFilePath = backupDir.resolve(dbFile.getFileName());
- try {
- Files.copy(dbFile, backupFilePath, StandardCopyOption.REPLACE_EXISTING);
- LOGGER.info("Database file copied to backup directory: {}", backupFilePath);
- } catch (IOException e) {
- LOGGER.error("Failed to copy database file to backup directory", e);
- throw new IOException("Error copying database file to backup directory", e);
- }
-
+ copyDatabaseFileToBackupDir(dbFile, backupDir);
performBackup(backupDir);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
@@ -500,21 +533,20 @@ public static List retrieveCommitDetails(List commits, P
* Shuts down the JGit components and optionally creates a backup.
*
* @param createBackup whether to create a backup before shutting down
- * @param originalPath the original path of the file to be backed up
*/
- private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath) {
+ private void shutdownGit(Path backupDir, boolean createBackup) {
// Unregister the listener and shut down the change filter
if (changeFilter != null) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
- LOGGER.info("Shut down change filter for file: {}", originalPath);
+ LOGGER.info("Shut down change filter");
}
// Shut down the executor if it's not already shut down
if (executor != null && !executor.isShutdown()) {
executor.shutdown();
- LOGGER.info("Shut down backup task for file: {}", originalPath);
+ LOGGER.info("Shut down backup task for file: {}");
}
// If backup is requested, ensure that we perform the Git-based backup
@@ -522,9 +554,9 @@ private void shutdownGit(Path backupDir, boolean createBackup, Path originalPath
try {
// Ensure the backup is a recent one by performing the Git commit
performBackup(backupDir);
- LOGGER.info("Backup created on shutdown for file: {}", originalPath);
+ LOGGER.info("Backup created on shutdown for file: {}");
} catch (IOException | GitAPIException e) {
- LOGGER.error("Error during Git backup on shutdown", e);
+ LOGGER.error("Error during Git backup on shutdown");
}
}
}
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index 375459d688d..a2d8e15bdf5 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -149,7 +149,7 @@ boolean saveAs(Path file, SaveDatabaseMode mode) throws GitAPIException, IOExcep
if (databasePath.isPresent()) {
// Close AutosaveManager, BackupManagerGit, and IndexManager for original library
AutosaveManager.shutdown(context);
- BackupManagerGit.shutdown(context, preferences.getFilePreferences().shouldCreateBackup(), databasePath.get());
+ BackupManagerGit.shutdown(context, preferences.getFilePreferences().shouldCreateBackup());
libraryTab.closeIndexManger();
}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerDiscardedTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerDiscardedTest.java
deleted file mode 100644
index 7619e0c9ae2..00000000000
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerDiscardedTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package org.jabref.gui.autosaveandbackup;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.jabref.gui.LibraryTab;
-import org.jabref.logic.exporter.AtomicFileWriter;
-import org.jabref.logic.exporter.BibDatabaseWriter;
-import org.jabref.logic.exporter.BibWriter;
-import org.jabref.logic.exporter.BibtexDatabaseWriter;
-import org.jabref.logic.exporter.SelfContainedSaveConfiguration;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.model.database.BibDatabase;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntry;
-import org.jabref.model.entry.BibEntryTypesManager;
-import org.jabref.model.entry.field.StandardField;
-import org.jabref.model.metadata.SaveOrder;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-import org.mockito.Answers;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.mock;
-
-/**
- * Test for "discarded" flag
- */
-class BackupManagerDiscardedTest {
-
- private BibDatabaseContext bibDatabaseContext;
- private BackupManager backupManager;
- private Path testBib;
- private SelfContainedSaveConfiguration saveConfiguration;
- private CliPreferences preferences;
- private BibEntryTypesManager bibEntryTypesManager;
- private Path backupDir;
-
- @BeforeEach
- void setup(@TempDir Path tempDir) throws Exception {
- this.backupDir = tempDir.resolve("backups");
- Files.createDirectories(backupDir);
-
- testBib = tempDir.resolve("test.bib");
-
- bibDatabaseContext = new BibDatabaseContext(new BibDatabase());
- bibDatabaseContext.setDatabasePath(testBib);
-
- bibEntryTypesManager = new BibEntryTypesManager();
- saveConfiguration = new SelfContainedSaveConfiguration(SaveOrder.getDefaultSaveOrder(), false, BibDatabaseWriter.SaveType.WITH_JABREF_META_DATA, false);
- preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
-
- saveDatabase();
-
- backupManager = new BackupManager(mock(LibraryTab.class), bibDatabaseContext, bibEntryTypesManager, preferences);
-
- makeBackup();
- }
-
- private void saveDatabase() throws IOException {
- try (Writer writer = new AtomicFileWriter(testBib, StandardCharsets.UTF_8, false)) {
- BibWriter bibWriter = new BibWriter(writer, bibDatabaseContext.getDatabase().getNewLineSeparator());
- new BibtexDatabaseWriter(
- bibWriter,
- saveConfiguration,
- preferences.getFieldPreferences(),
- preferences.getCitationKeyPatternPreferences(),
- bibEntryTypesManager)
- .saveDatabase(bibDatabaseContext);
- }
- }
-
- private void databaseModification() {
- bibDatabaseContext.getDatabase().insertEntry(new BibEntry().withField(StandardField.NOTE, "test"));
- }
-
- private void makeBackup() {
- backupManager.determineBackupPathForNewBackup(backupDir).ifPresent(path -> backupManager.performBackup(path));
- }
-
- @Test
- void noDiscardingAChangeLeadsToNewerBackupBeReported() throws Exception {
- databaseModification();
- makeBackup();
- assertTrue(BackupManager.backupFileDiffers(testBib, backupDir));
- }
-
- @Test
- void noDiscardingASavedChange() throws Exception {
- databaseModification();
- makeBackup();
- saveDatabase();
- assertFalse(BackupManager.backupFileDiffers(testBib, backupDir));
- }
-
- @Test
- void discardingAChangeLeadsToNewerBackupToBeIgnored() throws Exception {
- databaseModification();
- makeBackup();
- backupManager.discardBackup(backupDir);
- assertFalse(BackupManager.backupFileDiffers(testBib, backupDir));
- }
-}
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
deleted file mode 100644
index 3233cf47a13..00000000000
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java
+++ /dev/null
@@ -1,190 +0,0 @@
-package org.jabref.gui.autosaveandbackup;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.FileTime;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-
-import org.jabref.gui.LibraryTab;
-import org.jabref.logic.FilePreferences;
-import org.jabref.logic.preferences.CliPreferences;
-import org.jabref.logic.util.BackupFileType;
-import org.jabref.logic.util.Directories;
-import org.jabref.logic.util.io.BackupFileUtil;
-import org.jabref.model.database.BibDatabase;
-import org.jabref.model.database.BibDatabaseContext;
-import org.jabref.model.entry.BibEntryTypesManager;
-import org.jabref.model.groups.event.GroupUpdatedEvent;
-import org.jabref.model.metadata.MetaData;
-import org.jabref.model.metadata.event.MetaDataChangedEvent;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-import org.mockito.Answers;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-class BackupManagerTest {
-
- Path backupDir;
-
- @BeforeEach
- void setup(@TempDir Path tempDir) {
- backupDir = tempDir.resolve("backup");
- }
-
- @Test
- void backupFileNameIsCorrectlyGeneratedInAppDataDirectory() {
- Path bibPath = Path.of("tmp", "test.bib");
- backupDir = Directories.getBackupDirectory();
- Path bakPath = BackupManager.getBackupPathForNewBackup(bibPath, backupDir);
-
- // Pattern is "27182d3c--test.bib--", but the hashing is implemented differently on Linux than on Windows
- assertNotEquals("", bakPath);
- }
-
- @Test
- void backupFileIsEqualForNonExistingBackup() throws Exception {
- Path originalFile = Path.of(BackupManagerTest.class.getResource("no-autosave.bib").toURI());
- assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
- }
-
- @Test
- void backupFileIsEqual() throws Exception {
- // Prepare test: Create backup file on "right" path
- Path source = Path.of(BackupManagerTest.class.getResource("no-changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
- Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
-
- Path originalFile = Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI());
- assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
- }
-
- @Test
- void backupFileDiffers() throws Exception {
- // Prepare test: Create backup file on "right" path
- Path source = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
- Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
-
- Path originalFile = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- assertTrue(BackupManager.backupFileDiffers(originalFile, backupDir));
- }
-
- @Test
- void correctBackupFileDeterminedForMultipleBakFiles() throws Exception {
- Path noChangesBib = Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI());
- Path noChangesBibBak = Path.of(BackupManagerTest.class.getResource("no-changes.bib.bak").toURI());
-
- // Prepare test: Create backup files on "right" path
- // most recent file does not have any changes
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(noChangesBib, BackupFileType.BACKUP, backupDir);
- Files.copy(noChangesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
-
- // create "older" .bak files containing changes
- for (int i = 0; i < 10; i++) {
- Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- Path directory = backupDir;
- String timeSuffix = "2020-02-03--00.00.0" + Integer.toString(i);
- String fileName = BackupFileUtil.getUniqueFilePrefix(noChangesBib) + "--no-changes.bib--" + timeSuffix + ".bak";
- target = directory.resolve(fileName);
- Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
- }
-
- Path originalFile = noChangesBib;
- assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
- }
-
- @Test
- void bakFileWithNewerTimeStampLeadsToDiff() throws Exception {
- Path changesBib = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
-
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
- Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
-
- assertTrue(BackupManager.backupFileDiffers(changesBib, backupDir));
- }
-
- @Test
- void bakFileWithOlderTimeStampDoesNotLeadToDiff() throws Exception {
- Path changesBib = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
-
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
- Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
-
- // Make .bak file very old
- Files.setLastModifiedTime(target, FileTime.fromMillis(0));
-
- assertFalse(BackupManager.backupFileDiffers(changesBib, backupDir));
- }
-
- @Test
- void shouldNotCreateABackup(@TempDir Path customDir) throws Exception {
- Path backupDir = customDir.resolve("subBackupDir");
- Files.createDirectories(backupDir);
-
- var database = new BibDatabaseContext(new BibDatabase());
- database.setDatabasePath(customDir.resolve("Bibfile.bib"));
-
- var preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
- var filePreferences = mock(FilePreferences.class);
- when(preferences.getFilePreferences()).thenReturn(filePreferences);
- when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
- when(filePreferences.shouldCreateBackup()).thenReturn(false);
-
- BackupManager manager = BackupManager.start(
- mock(LibraryTab.class),
- database,
- mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS),
- preferences);
- manager.listen(new MetaDataChangedEvent(new MetaData()));
-
- BackupManager.shutdown(database, filePreferences.getBackupDirectory(), filePreferences.shouldCreateBackup());
-
- List files = Files.list(backupDir).toList();
- assertEquals(Collections.emptyList(), files);
- }
-
- @Test
- void shouldCreateABackup(@TempDir Path customDir) throws Exception {
- Path backupDir = customDir.resolve("subBackupDir");
- Files.createDirectories(backupDir);
-
- var database = new BibDatabaseContext(new BibDatabase());
- database.setDatabasePath(customDir.resolve("Bibfile.bib"));
-
- var preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
- var filePreferences = mock(FilePreferences.class);
- when(preferences.getFilePreferences()).thenReturn(filePreferences);
- when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
- when(filePreferences.shouldCreateBackup()).thenReturn(true);
-
- BackupManager manager = BackupManager.start(
- mock(LibraryTab.class),
- database,
- mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS),
- preferences);
- manager.listen(new MetaDataChangedEvent(new MetaData()));
-
- Optional fullBackupPath = manager.determineBackupPathForNewBackup(backupDir);
- fullBackupPath.ifPresent(manager::performBackup);
- manager.listen(new GroupUpdatedEvent(new MetaData()));
-
- BackupManager.shutdown(database, backupDir, true);
-
- List files = Files.list(backupDir).sorted().toList();
- // we only know the first backup path because the second one is created on shutdown
- // due to timing issues we cannot test that reliable
- assertEquals(fullBackupPath.get(), files.getFirst());
- }
-}
From 3532d2eb9881fd7c99b5f08b371c84a6dff3703f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 4 Dec 2024 16:12:05 +0100
Subject: [PATCH 58/84] The popup is available again
---
src/main/java/org/jabref/gui/LibraryTab.java | 1 +
.../autosaveandbackup/BackupManagerGit.java | 123 +++++++++---------
.../gui/exporter/SaveDatabaseAction.java | 2 +-
.../importer/actions/OpenDatabaseAction.java | 2 +-
4 files changed, 64 insertions(+), 64 deletions(-)
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index b27c7c70f33..5c2041a8eb3 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -803,6 +803,7 @@ private void onClosed(Event event) {
}
try {
BackupManagerGit.shutdown(bibDatabaseContext,
+ preferences.getFilePreferences().getBackupDirectory(),
preferences.getFilePreferences().shouldCreateBackup());
} catch (RuntimeException e) {
LOGGER.error("Problem when shutting down backup manager", e);
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 44ecb6f71bc..77db0b20b98 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -80,18 +80,17 @@ public class BackupManagerGit {
changeFilter = new CoarseChangeFilter(bibDatabaseContext);
changeFilter.registerListener(this);
- Path backupDirPath = preferences.getFilePreferences().getBackupDirectory();
- LOGGER.info("Backup directory path: {}", backupDirPath);
+ LOGGER.info("Backup directory path: {}", preferences.getFilePreferences().getBackupDirectory());
- ensureGitInitialized(backupDirPath);
+ ensureGitInitialized(preferences.getFilePreferences().getBackupDirectory());
- File backupDir = backupDirPath.toFile();
- if (!backupDir.exists() && !backupDir.mkdirs()) {
- LOGGER.error("Failed to create backup directory: {}", backupDir);
- throw new IOException("Unable to create backup directory: " + backupDir);
+ File backupDirFile = preferences.getFilePreferences().getBackupDirectory().toFile();
+ if (!backupDirFile.exists() && !backupDirFile.mkdirs()) {
+ LOGGER.error("Failed to create backup directory: {}", preferences.getFilePreferences().getBackupDirectory());
+ throw new IOException("Unable to create backup directory: " + preferences.getFilePreferences().getBackupDirectory());
}
- copyDatabaseFileToBackupDir(dbFile, backupDirPath);
+ copyDatabaseFileToBackupDir(dbFile, preferences.getFilePreferences().getBackupDirectory());
}
/**
@@ -101,7 +100,7 @@ public class BackupManagerGit {
* @param uuid The UUID to append.
* @return The modified file name with the UUID (e.g., library_123e4567-e89b-12d3-a456-426614174000.bib).
*/
- private String appendUuidToFileName(String originalFileName, String uuid) {
+ private static String appendUuidToFileName(String originalFileName, String uuid) {
int dotIndex = originalFileName.lastIndexOf('.');
if (dotIndex == -1) {
// If there's no extension, just append the UUID
@@ -122,7 +121,7 @@ private String appendUuidToFileName(String originalFileName, String uuid) {
* @return The UUID associated with the file.
* @throws IOException If an error occurs while accessing or creating the UUID.
*/
- private String getOrGenerateFileUuid(Path filePath) throws IOException {
+ private static String getOrGenerateFileUuid(Path filePath) throws IOException {
// Define a hidden metadata file to store the UUID
Path metadataFile = filePath.resolveSibling("." + filePath.getFileName().toString() + ".uuid");
@@ -186,7 +185,7 @@ static void ensureGitInitialized(Path backupDir) throws IOException, GitAPIExcep
}
// Helper method to copy the database file to the backup directory
- private void copyDatabaseFileToBackupDir(Path dbFile, Path backupDirPath) throws IOException {
+ private static void copyDatabaseFileToBackupDir(Path dbFile, Path backupDirPath) throws IOException {
String fileUuid = getOrGenerateFileUuid(dbFile);
String uniqueFileName = appendUuidToFileName(dbFile.getFileName().toString(), fileUuid);
Path backupFilePath = backupDirPath.resolve(uniqueFileName);
@@ -198,6 +197,8 @@ private void copyDatabaseFileToBackupDir(Path dbFile, Path backupDirPath) throws
}
}
+ // A method
+
/**
* Starts a new BackupManagerGit instance and begins the backup task.
*
@@ -224,10 +225,12 @@ public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext b
* @param bibDatabaseContext the BibDatabaseContext
* @param createBackup whether to create a backup before shutting down
*/
- public static void shutdown(BibDatabaseContext bibDatabaseContext, boolean createBackup) {
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
runningInstances.stream()
.filter(instance -> instance.bibDatabaseContext == bibDatabaseContext)
- .forEach(backupManager -> backupManager.shutdownGit(bibDatabaseContext.getDatabasePath().orElseThrow().getParent().resolve("backup"), createBackup));
+ .forEach(backupManager -> backupManager.shutdownGit(bibDatabaseContext,
+ backupDir,
+ createBackup));
// Remove the instances associated with the BibDatabaseContext after shutdown
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
@@ -247,7 +250,7 @@ void startBackupTask(Path backupDir, BibDatabaseContext bibDatabaseContext) {
try {
Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
copyDatabaseFileToBackupDir(dbFile, backupDir);
- performBackup(backupDir);
+ performBackup(dbFile, backupDir);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
}
@@ -262,13 +265,14 @@ void startBackupTask(Path backupDir, BibDatabaseContext bibDatabaseContext) {
* Performs the backup by checking for changes and committing them to the Git repository.
*
* @param backupDir the backup directory
+ * @param dbfile the database file
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
- protected void performBackup(Path backupDir) throws IOException, GitAPIException {
+ protected void performBackup(Path dbfile, Path backupDir) throws IOException, GitAPIException {
- boolean needsCommit = backupGitDiffers(backupDir);
+ boolean needsCommit = backupGitDiffers(dbfile, backupDir);
if (!needsBackup && !needsCommit) {
LOGGER.info("No changes detected, beacuse needsBackup is :" + needsBackup + " and needsCommit is :" + needsCommit);
@@ -337,9 +341,13 @@ public static void restoreBackup(Path backupDir, ObjectId objectId) {
* @throws GitAPIException if a Git API error occurs
*/
- public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAPIException {
+ public static boolean backupGitDiffers(Path dbFile, Path backupDir) throws IOException, GitAPIException {
+
+ // Ensure the specific database file is copied to the backup directory
+ copyDatabaseFileToBackupDir(dbFile, backupDir);
+
// Ensure the Git repository exists
- LOGGER.info("Checking if backup differs for directory: {}", backupDir);
+ LOGGER.info("Checking if backup differs for file: {}", dbFile);
// Open the Git repository located in the backup directory
Repository repository = openGitRepository(backupDir);
@@ -352,53 +360,40 @@ public static boolean backupGitDiffers(Path backupDir) throws IOException, GitAP
}
LOGGER.info("HEAD commit ID: {}", headCommitId.getName());
- // Iterate over the files in the backup directory to check if they differ from the repository
- try (Stream paths = Files.walk(backupDir)) {
- for (Path path : paths.filter(Files::isRegularFile).toList()) {
- // Ignore non-.bib files (e.g., .DS_Store)
- if (!path.toString().endsWith(".bib")) {
- continue; // Skip .bib files
- }
-
- // Skip .git directory files
- if (path.toString().contains(".git")) {
- continue;
- }
-
- // Calculate the relative path in the Git repository
- Path relativePath = backupDir.relativize(path);
- LOGGER.info("Checking file: {}", relativePath);
-
- try {
+ // Compute the repository file name using the naming convention (filename + UUID)
+ String baseName = dbFile.getFileName().toString();
+ String uuid = getOrGenerateFileUuid(dbFile); // Generate or retrieve the UUID for this file
+ String repoFileName = baseName.replace(".bib", "") + "_" + uuid + ".bib";
+ Path relativePath = Path.of(repoFileName);
+ LOGGER.info("Checking repository file: {}", relativePath);
- // Check if the file exists in the latest commit
- ObjectId objectId = repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/"));
- if (objectId == null) {
- LOGGER.info("File not found in the latest commit: {}. Assuming it differs.", relativePath);
- return true;
- }
+ try {
+ // Check if the file exists in the latest commit
+ ObjectId objectId = repository.resolve("HEAD:" + relativePath.toString().replace("\\", "/"));
+ if (objectId == null) {
+ LOGGER.info("File not found in the latest commit: {}. Assuming it differs.", relativePath);
+ return true;
+ }
- // Compare the content of the file in the Git repository with the current file
- ObjectLoader loader = repository.open(objectId);
- String committedContent = normalizeBibTeX(new String(loader.getBytes(), StandardCharsets.UTF_8));
- String currentContent = normalizeBibTeX(Files.readString(path, StandardCharsets.UTF_8));
- LOGGER.info("Committed content: {}", committedContent);
- LOGGER.info("Current content: {}", currentContent);
-
- // If the contents differ, return true
- if (!currentContent.equals(committedContent)) {
- LOGGER.info("Content differs for file: {}", relativePath);
- return true;
- }
- } catch (MissingObjectException e) {
- // If the file is missing from the commit, assume it differs
- LOGGER.info("File not found in the latest commit: {}. Assuming it differs.", relativePath);
- return true;
- }
+ // Compare the content of the file in the Git repository with the current file
+ ObjectLoader loader = repository.open(objectId);
+ String committedContent = normalizeBibTeX(new String(loader.getBytes(), StandardCharsets.UTF_8));
+ String currentContent = normalizeBibTeX(Files.readString(dbFile, StandardCharsets.UTF_8));
+ LOGGER.info("Committed content: {}", committedContent);
+ LOGGER.info("Current content: {}", currentContent);
+
+ // If the contents differ, return true
+ if (!currentContent.equals(committedContent)) {
+ LOGGER.info("Content differs for file: {}", relativePath);
+ return true;
}
+ } catch (MissingObjectException e) {
+ // If the file is missing from the commit, assume it differs
+ LOGGER.info("File not found in the latest commit: {}. Assuming it differs.", relativePath);
+ return true;
}
- LOGGER.info("No differences found in the backup.");
+ LOGGER.info("No differences found for the file: {}", dbFile);
return false; // No differences found
}
@@ -533,9 +528,11 @@ public static List retrieveCommitDetails(List commits, P
* Shuts down the JGit components and optionally creates a backup.
*
* @param createBackup whether to create a backup before shutting down
+ * @param backupDir the backup directory
+ * @param bibDatabaseContext the BibDatabaseContext
*/
- private void shutdownGit(Path backupDir, boolean createBackup) {
+ private void shutdownGit(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
// Unregister the listener and shut down the change filter
if (changeFilter != null) {
changeFilter.unregisterListener(this);
@@ -552,8 +549,10 @@ private void shutdownGit(Path backupDir, boolean createBackup) {
// If backup is requested, ensure that we perform the Git-based backup
if (createBackup) {
try {
+ // Get the file path of the database
+ Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
// Ensure the backup is a recent one by performing the Git commit
- performBackup(backupDir);
+ performBackup(dbFile, backupDir);
LOGGER.info("Backup created on shutdown for file: {}");
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during Git backup on shutdown");
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index a2d8e15bdf5..727fc394eff 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -149,7 +149,7 @@ boolean saveAs(Path file, SaveDatabaseMode mode) throws GitAPIException, IOExcep
if (databasePath.isPresent()) {
// Close AutosaveManager, BackupManagerGit, and IndexManager for original library
AutosaveManager.shutdown(context);
- BackupManagerGit.shutdown(context, preferences.getFilePreferences().shouldCreateBackup());
+ BackupManagerGit.shutdown(context, preferences.getFilePreferences().getBackupDirectory(), preferences.getFilePreferences().shouldCreateBackup());
libraryTab.closeIndexManger();
}
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
index 2c2b14f5f27..e1acb386292 100644
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
@@ -266,7 +266,7 @@ private ParserResult loadDatabase(Path file) throws Exception {
}
ParserResult parserResult = null;
- if (BackupManagerGit.backupGitDiffers(backupDir)) {
+ if (BackupManagerGit.backupGitDiffers(fileToLoad, backupDir)) {
// In case the backup differs, ask the user what to do.
LOGGER.info("Backup differs from saved file, ask the user what to do");
// In case the user opted for restoring a backup, the content of the backup is contained in parserResult.
From 08b3c0c7e9f9c8386cae1fb2ef62379f80d9a815 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 4 Dec 2024 17:44:59 +0100
Subject: [PATCH 59/84] Restore backup that functions
---
.../autosaveandbackup/BackupManagerGit.java | 61 ++++++++++++++-----
.../jabref/gui/dialogs/BackupUIManager.java | 4 +-
2 files changed, 47 insertions(+), 18 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 77db0b20b98..e0fc41f8c64 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -2,6 +2,7 @@
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -137,6 +138,25 @@ private static String getOrGenerateFileUuid(Path filePath) throws IOException {
return uuid;
}
+ /**
+ * Rewrites the content of the file at the specified path with the given string.
+ *
+ * @param dbFile The path to the file to be rewritten.
+ * @param content The string content to write into the file.
+ * @throws IOException If an I/O error occurs during the write operation.
+ */
+ public static void rewriteFile(Path dbFile, String content) throws IOException {
+ // Ensure the file exists before rewriting
+ if (!Files.exists(dbFile)) {
+ throw new FileNotFoundException("The file at path " + dbFile + " does not exist.");
+ }
+
+ // Write the new content to the file (overwrite mode)
+ Files.writeString(dbFile, content, StandardCharsets.UTF_8);
+
+ LOGGER.info("Successfully rewrote the file at path: {}", dbFile);
+ }
+
// Helper method to normalize BibTeX content
private static String normalizeBibTeX(String input) {
if (input == null || input.isBlank()) {
@@ -311,24 +331,33 @@ public synchronized void listen(BibDatabaseContextChangedEvent event) {
* @param objectId the commit ID to restore from
*/
- public static void restoreBackup(Path backupDir, ObjectId objectId) {
- try {
- Git git = Git.open(backupDir.toFile());
-
- git.checkout().setStartPoint(objectId.getName()).setAllPaths(true).call();
- LOGGER.info("checkout done");
-
- // Add commits to staging Area
- git.add().addFilepattern(".").call();
+ public static void restoreBackup(Path dbFile, Path backupDir, ObjectId objectId) {
+ try (Repository repository = openGitRepository(backupDir);
+ Git git = new Git(repository)) {
+ // Resolve the filename of dbFile in the repository
+ String baseName = dbFile.getFileName().toString();
+ String uuid = getOrGenerateFileUuid(dbFile); // Generate or retrieve the UUID for this file
+ String relativeFilePath = baseName.replace(".bib", "") + "_" + uuid + ".bib";
+ LOGGER.info("Relative file path TO RESTORE: {}", relativeFilePath);
+ String gitPath = backupDir.relativize(backupDir.resolve(relativeFilePath)).toString().replace("\\", "/");
+
+ LOGGER.info("Restoring file: {}", gitPath);
+
+ // Load the content of the file from the specified commit
+ ObjectId fileObjectId = repository.resolve(objectId.getName() + ":" + gitPath);
+ if (fileObjectId == null) {
+ throw new IllegalArgumentException("File not found in commit: " + objectId.getName());
+ }
- // Commit with a message
- git.commit().setMessage("Restored content from commit: " + objectId.getName()).call();
+ // Read the content of the file from the Git object
+ ObjectLoader loader = repository.open(fileObjectId);
+ String fileContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
- LOGGER.info("Restored backup from Git repository and committed the changes");
- } catch (
- IOException |
- GitAPIException e) {
- LOGGER.error("Error while restoring the backup", e);
+ // Rewrite the original file at dbFile path
+ rewriteFile(dbFile, fileContent);
+ LOGGER.info("Restored content to: {}", dbFile);
+ } catch (IOException | IllegalArgumentException e) {
+ LOGGER.error("Error while restoring the backup: {}", e.getMessage(), e);
}
}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 4e328cc6c4d..0dec3bf796e 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -69,7 +69,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
List backups = BackupManagerGit.retrieveCommitDetails(commits, preferences.getFilePreferences().getBackupDirectory()).reversed();
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
ObjectId commitId = backups.getFirst().getId();
- BackupManagerGit.restoreBackup(preferences.getFilePreferences().getBackupDirectory(), commitId);
+ BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory(), commitId);
return Optional.empty();
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
@@ -81,7 +81,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
if (recordBackupChoice.get().action() == BackupChoiceDialog.RESTORE_BACKUP) {
LOGGER.warn(recordBackupChoice.get().entry().getSize());
ObjectId commitId = recordBackupChoice.get().entry().getId();
- BackupManagerGit.restoreBackup(preferences.getFilePreferences().getBackupDirectory(), commitId);
+ BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory(), commitId);
return Optional.empty();
}
if (recordBackupChoice.get().action() == BackupChoiceDialog.REVIEW_BACKUP) {
From 2c6da4b8b4d610e28bcf265986b55c3597017642 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 4 Dec 2024 19:16:07 +0100
Subject: [PATCH 60/84] Corrected retrieve commits / commits details
---
.../autosaveandbackup/BackupManagerGit.java | 146 +++++++++++++-----
.../jabref/gui/dialogs/BackupUIManager.java | 4 +-
2 files changed, 109 insertions(+), 41 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index e0fc41f8c64..ec30593cc72 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -11,7 +11,6 @@
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
@@ -332,8 +331,7 @@ public synchronized void listen(BibDatabaseContextChangedEvent event) {
*/
public static void restoreBackup(Path dbFile, Path backupDir, ObjectId objectId) {
- try (Repository repository = openGitRepository(backupDir);
- Git git = new Git(repository)) {
+ try (Repository repository = openGitRepository(backupDir)) {
// Resolve the filename of dbFile in the repository
String baseName = dbFile.getFileName().toString();
String uuid = getOrGenerateFileUuid(dbFile); // Generate or retrieve the UUID for this file
@@ -345,8 +343,8 @@ public static void restoreBackup(Path dbFile, Path backupDir, ObjectId objectId)
// Load the content of the file from the specified commit
ObjectId fileObjectId = repository.resolve(objectId.getName() + ":" + gitPath);
- if (fileObjectId == null) {
- throw new IllegalArgumentException("File not found in commit: " + objectId.getName());
+ if (fileObjectId == null) { // File not found in the commit
+ performBackupNoCommits(dbFile, backupDir);
}
// Read the content of the file from the Git object
@@ -356,7 +354,10 @@ public static void restoreBackup(Path dbFile, Path backupDir, ObjectId objectId)
// Rewrite the original file at dbFile path
rewriteFile(dbFile, fileContent);
LOGGER.info("Restored content to: {}", dbFile);
- } catch (IOException | IllegalArgumentException e) {
+ } catch (
+ IOException |
+ IllegalArgumentException |
+ GitAPIException e) {
LOGGER.error("Error while restoring the backup: {}", e.getMessage(), e);
}
}
@@ -385,7 +386,9 @@ public static boolean backupGitDiffers(Path dbFile, Path backupDir) throws IOExc
ObjectId headCommitId = repository.resolve("HEAD");
if (headCommitId == null) {
LOGGER.info("No commits found in the repository. Assuming the file differs.");
- return true;
+ // perform a commit
+ performBackupNoCommits(dbFile, backupDir);
+ return false;
}
LOGGER.info("HEAD commit ID: {}", headCommitId.getName());
@@ -438,7 +441,7 @@ private static Repository openGitRepository(Path backupDir) throws IOException {
/**
* Shows the differences between the specified commit and the latest commit.
*
- * @param originalPath the original path of the file
+ * @param dbFile the path of the file
* @param backupDir the backup directory
* @param commitId the commit ID to compare with the latest commit
* @return a list of DiffEntry objects representing the differences
@@ -446,7 +449,7 @@ private static Repository openGitRepository(Path backupDir) throws IOException {
* @throws GitAPIException if a Git API error occurs
*/
- public List showDiffers(Path originalPath, Path backupDir, String commitId) throws IOException, GitAPIException {
+ public List showDiffers(Path dbFile, Path backupDir, String commitId) throws IOException, GitAPIException {
File repoDir = backupDir.toFile();
Repository repository = new FileRepositoryBuilder()
@@ -468,6 +471,7 @@ need a class to show the last ten backups indicating: date/ size/ number of entr
/**
* Retrieves the last n commits from the Git repository.
*
+ * @param dbFile the database file
* @param backupDir the backup directory
* @param n the number of commits to retrieve
* @return a list of RevCommit objects representing the commits
@@ -475,8 +479,15 @@ need a class to show the last ten backups indicating: date/ size/ number of entr
* @throws GitAPIException if a Git API error occurs
*/
- public static List retrieveCommits(Path backupDir, int n) throws IOException, GitAPIException {
+ public static List retrieveCommits(Path dbFile, Path backupDir, int n) throws IOException, GitAPIException {
List retrievedCommits = new ArrayList<>();
+
+ // Compute the repository file name using the naming convention (filename + UUID)
+ String baseName = dbFile.getFileName().toString();
+ String uuid = getOrGenerateFileUuid(dbFile); // Generate or retrieve the UUID for this file
+ String repoFileName = baseName.replace(".bib", "") + "_" + uuid + ".bib";
+ String dbFileRelativePath = backupDir.relativize(backupDir.resolve(repoFileName)).toString().replace("\\", "/");
+
// Open Git repository
try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
// Use RevWalk to traverse commits
@@ -486,66 +497,96 @@ public static List retrieveCommits(Path backupDir, int n) throws IOEx
int count = 0;
for (RevCommit commit : revWalk) {
- retrievedCommits.add(commit);
- count++;
- if (count == n) {
- break; // Stop after collecting the required number of commits
+ // Check if this commit involves the dbFile
+ try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.addTree(commit.getTree());
+ treeWalk.setRecursive(true);
+
+ boolean fileFound = false;
+ while (treeWalk.next()) {
+ if (treeWalk.getPathString().equals(dbFileRelativePath)) {
+ fileFound = true;
+ break;
+ }
+ }
+
+ if (fileFound) {
+ retrievedCommits.add(commit);
+ count++;
+ if (count == n) {
+ break; // Stop after collecting the required number of commits
+ }
+ }
}
}
}
}
- // Reverse the list to have commits in the correct order
- Collections.reverse(retrievedCommits);
return retrievedCommits;
}
/**
- * Retrieves detailed information about the specified commits.
+ * Retrieves detailed information about the specified commits, focusing on the target file.
*
* @param commits the list of commits to retrieve details for
+ * @param dbFile the target file to retrieve details about
* @param backupDir the backup directory
- * @return a list of lists, each containing details about a commit
+ * @return a list of BackupEntry objects containing details about each commit
* @throws IOException if an I/O error occurs
* @throws GitAPIException if a Git API error occurs
*/
+ public static List retrieveCommitDetails(List commits, Path dbFile, Path backupDir) throws IOException, GitAPIException {
+ List commitDetails = new ArrayList<>();
- public static List retrieveCommitDetails(List commits, Path backupDir) throws IOException, GitAPIException {
- List commitDetails;
- try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
- commitDetails = new ArrayList<>();
+ // Compute the repository file name using the naming convention (filename + UUID)
+ String baseName = dbFile.getFileName().toString();
+ String uuid = getOrGenerateFileUuid(dbFile); // Generate or retrieve the UUID for this file
+ String repoFileName = baseName.replace(".bib", "") + "_" + uuid + ".bib";
+ String dbFileRelativePath = backupDir.relativize(backupDir.resolve(repoFileName)).toString().replace("\\", "/");
+ try (Repository repository = Git.open(backupDir.toFile()).getRepository()) {
// Browse the list of commits given as a parameter
for (RevCommit commit : commits) {
- // A list to stock details about the commit
- List commitInfo = new ArrayList<>();
- commitInfo.add(commit.getName()); // ID of commit
+ // Variables to store commit-specific details
+ String sizeFormatted = "0 KB";
+ long fileSize = 0;
+ boolean fileFound = false;
- // Get the size of files changes by the commit
- String sizeFormatted;
+ // Use TreeWalk to find the target file in the commit
try (TreeWalk treeWalk = new TreeWalk(repository)) {
treeWalk.addTree(commit.getTree());
treeWalk.setRecursive(true);
- long totalSize = 0;
while (treeWalk.next()) {
- ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
- totalSize += loader.getSize(); // size in bytes
+ if (treeWalk.getPathString().equals(dbFileRelativePath)) {
+ // Calculate size of the target file
+ ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
+ fileSize = loader.getSize();
+ fileFound = true;
+ break;
+ }
}
- // Convert the size to Kb or Mb
- sizeFormatted = (totalSize > 1024 * 1024)
- ? String.format("%.2f Mo", totalSize / (1024.0 * 1024.0))
- : String.format("%.2f Ko", totalSize / 1024.0);
+ // Convert size to KB or MB
+ sizeFormatted = (fileSize > 1024 * 1024)
+ ? String.format("%.2f MB", fileSize / (1024.0 * 1024.0))
+ : String.format("%.2f KB", fileSize / 1024.0);
+ }
- commitInfo.add(sizeFormatted); // Add Formatted size
+ // Skip this commit if the file was not found
+ if (!fileFound) {
+ continue;
}
- // adding date detail
+ // Add commit details
Date date = commit.getAuthorIdent().getWhen();
- commitInfo.add(date.toString());
- // Add list of details to the main list
- BackupEntry backupEntry = new BackupEntry(ObjectId.fromString(commit.getName()), commitInfo.get(0), commitInfo.get(2), commitInfo.get(1), 0);
+ BackupEntry backupEntry = new BackupEntry(
+ ObjectId.fromString(commit.getName()), // Commit ID
+ commit.getName(), // Commit ID as string
+ date.toString(), // Commit date
+ sizeFormatted, // Formatted file size
+ 1 // Number of relevant .bib files (always 1 for dbFile)
+ );
commitDetails.add(backupEntry);
}
}
@@ -553,6 +594,33 @@ public static List retrieveCommitDetails(List commits, P
return commitDetails;
}
+ public static void performBackupNoCommits(Path dbFile, Path backupDir) throws IOException, GitAPIException {
+
+ LOGGER.info("No commits found in the repository. We need a first commit.");
+ // Ensure the specific database file is copied to the backup directory
+ copyDatabaseFileToBackupDir(dbFile, backupDir);
+
+ // Ensure the Git repository exists
+ LOGGER.info("Checking if backup differs for file: {}", dbFile);
+ ensureGitInitialized(backupDir);
+
+ // Open the Git repository located in the backup directory
+ Repository repository = openGitRepository(backupDir);
+
+ // Get the file name of the database file
+ String baseName = dbFile.getFileName().toString();
+ String uuid = getOrGenerateFileUuid(dbFile); // Generate or retrieve the UUID for this file
+ String repoFileName = baseName.replace(".bib", "") + "_" + uuid + ".bib";
+
+ // Stage the file for commit
+ git.add().addFilepattern(repoFileName).call();
+
+ // Commit the staged changes
+ RevCommit commit = git.commit()
+ .setMessage("Backup at " + Instant.now().toString())
+ .call();
+ }
+
/**
* Shuts down the JGit components and optionally creates a backup.
*
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 0dec3bf796e..c43e6a95a91 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -65,8 +65,8 @@ public static Optional showRestoreBackupDialog(DialogService dialo
return actionOpt.flatMap(action -> {
try {
- List commits = BackupManagerGit.retrieveCommits(preferences.getFilePreferences().getBackupDirectory(), -1);
- List backups = BackupManagerGit.retrieveCommitDetails(commits, preferences.getFilePreferences().getBackupDirectory()).reversed();
+ List commits = BackupManagerGit.retrieveCommits(originalPath, preferences.getFilePreferences().getBackupDirectory(), -1);
+ List backups = BackupManagerGit.retrieveCommitDetails(commits, originalPath, preferences.getFilePreferences().getBackupDirectory()).reversed();
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
ObjectId commitId = backups.getFirst().getId();
BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory(), commitId);
From bca3558813bfa500a7b697166e15eabe4f7a7709 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 4 Dec 2024 19:41:14 +0100
Subject: [PATCH 61/84] Updated submodule abbrv.jabref.org
---
buildres/abbrv.jabref.org | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/buildres/abbrv.jabref.org b/buildres/abbrv.jabref.org
index d87037495de..0fdf99147a8 160000
--- a/buildres/abbrv.jabref.org
+++ b/buildres/abbrv.jabref.org
@@ -1 +1 @@
-Subproject commit d87037495de7213b896dbb6a20170387de170709
+Subproject commit 0fdf99147a8a5fc8ae7ccd79ad4e0029e736e4a3
From 7e63aa2af9217dddfda8b767b205ca1a35fab1b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Wed, 4 Dec 2024 19:56:56 +0100
Subject: [PATCH 62/84] Updated submodule csl-styles to latest commit
---
src/main/resources/csl-styles | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles
index b413a778b81..49af15c4f5b 160000
--- a/src/main/resources/csl-styles
+++ b/src/main/resources/csl-styles
@@ -1 +1 @@
-Subproject commit b413a778b8170cf5aebbb9aeffec62cfd068e19e
+Subproject commit 49af15c4f5bca025b6b18ca48c447016586f01e7
From 97caf3408fb51c28679bef02e222f68ced11faa0 Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Wed, 4 Dec 2024 22:35:10 +0100
Subject: [PATCH 63/84] Added support to review each change in a backup
---
.../autosaveandbackup/BackupManagerGit.java | 46 ++++++++++++++++++-
.../jabref/gui/dialogs/BackupUIManager.java | 24 ++++++----
2 files changed, 61 insertions(+), 9 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index ec30593cc72..9dbcc0d60b4 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -429,6 +429,51 @@ public static boolean backupGitDiffers(Path dbFile, Path backupDir) throws IOExc
return false; // No differences found
}
+ public static Path getBackupFilePath(Path dbFile, Path backupDir) {
+ try {
+ String baseName = dbFile.getFileName().toString();
+ String uuid = getOrGenerateFileUuid(dbFile);
+ String relativeFileName = baseName.replace(".bib", "") + "_" + uuid + ".bib";
+ return backupDir.resolve(relativeFileName);
+ } catch (
+ IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void writeBackupFileToCommit(Path dbFile, Path backupDir, ObjectId objectId) {
+ try (Repository repository = openGitRepository(backupDir)) {
+ // Resolve the filename of dbFile in the repository
+ String baseName = dbFile.getFileName().toString();
+ String uuid = getOrGenerateFileUuid(dbFile); // Generate or retrieve the UUID for this file
+ String relativeFilePath = baseName.replace(".bib", "") + "_" + uuid + ".bib";
+ LOGGER.info("Relative file path TO RESTORE: {}", relativeFilePath);
+ String gitPath = backupDir.relativize(backupDir.resolve(relativeFilePath)).toString().replace("\\", "/");
+
+ LOGGER.info("Restoring file: {}", gitPath);
+
+ // Load the content of the file from the specified commit
+ ObjectId fileObjectId = repository.resolve(objectId.getName() + ":" + gitPath);
+ if (fileObjectId == null) { // File not found in the commit
+ performBackupNoCommits(dbFile, backupDir);
+ }
+
+ // Read the content of the file from the Git object
+ ObjectLoader loader = repository.open(fileObjectId);
+ String fileContent = new String(loader.getBytes(), StandardCharsets.UTF_8);
+
+ Path backupFilePath = getBackupFilePath(dbFile, backupDir);
+ // Rewrite the original file at backupFilePath path
+ rewriteFile(backupFilePath, fileContent);
+ LOGGER.info("Restored content to: {}", dbFile);
+ } catch (
+ IOException |
+ IllegalArgumentException |
+ GitAPIException e) {
+ LOGGER.error("Error while restoring the backup: {}", e.getMessage(), e);
+ }
+ }
+
private static Repository openGitRepository(Path backupDir) throws IOException {
FileRepositoryBuilder builder = new FileRepositoryBuilder();
// Initialize Git repository from the backup directory
@@ -628,7 +673,6 @@ public static void performBackupNoCommits(Path dbFile, Path backupDir) throws IO
* @param backupDir the backup directory
* @param bibDatabaseContext the BibDatabaseContext
*/
-
private void shutdownGit(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
// Unregister the listener and shut down the change filter
if (changeFilter != null) {
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index c43e6a95a91..8c7333ebf83 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -29,8 +29,6 @@
import org.jabref.logic.importer.OpenDatabase;
import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.l10n.Localization;
-import org.jabref.logic.util.BackupFileType;
-import org.jabref.logic.util.io.BackupFileUtil;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.util.DummyFileUpdateMonitor;
import org.jabref.model.util.FileUpdateMonitor;
@@ -72,7 +70,8 @@ public static Optional showRestoreBackupDialog(DialogService dialo
BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory(), commitId);
return Optional.empty();
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
- return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
+ ObjectId commitId = backups.getFirst().getId();
+ return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager, commitId, commitId);
} else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
var recordBackupChoice = showBackupChoiceDialog(dialogService, originalPath, preferences, backups);
if (recordBackupChoice.isEmpty()) {
@@ -86,7 +85,9 @@ public static Optional showRestoreBackupDialog(DialogService dialo
}
if (recordBackupChoice.get().action() == BackupChoiceDialog.REVIEW_BACKUP) {
LOGGER.warn(recordBackupChoice.get().entry().getSize());
- return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
+ ObjectId latestCommitId = backups.getFirst().getId();
+ ObjectId commitId = recordBackupChoice.get().entry().getId();
+ return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager, commitId, latestCommitId);
}
}
} catch (
@@ -119,7 +120,9 @@ private static Optional showReviewBackupDialog(
GuiPreferences preferences,
FileUpdateMonitor fileUpdateMonitor,
UndoManager undoManager,
- StateManager stateManager) {
+ StateManager stateManager,
+ ObjectId commitIdToReview,
+ ObjectId latestCommitId) {
try {
ImportFormatPreferences importFormatPreferences = preferences.getImportFormatPreferences();
@@ -128,9 +131,13 @@ private static Optional showReviewBackupDialog(
// This will be modified by using the `DatabaseChangesResolverDialog`.
BibDatabaseContext originalDatabase = originalParserResult.getDatabaseContext();
- Path backupPath = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, preferences.getFilePreferences().getBackupDirectory()).orElseThrow();
- LOGGER.info("Ligne 127, BackupUIManager, Loading backup database from {}", backupPath);
- BibDatabaseContext backupDatabase = OpenDatabase.loadDatabase(backupPath, importFormatPreferences, new DummyFileUpdateMonitor()).getDatabaseContext();
+ Path backupPath = preferences.getFilePreferences().getBackupDirectory();
+
+ BackupManagerGit.writeBackupFileToCommit(originalPath, backupPath, commitIdToReview);
+
+ Path backupFilePath = BackupManagerGit.getBackupFilePath(originalPath, backupPath);
+
+ BibDatabaseContext backupDatabase = OpenDatabase.loadDatabase(backupFilePath, importFormatPreferences, new DummyFileUpdateMonitor()).getDatabaseContext();
DatabaseChangeResolverFactory changeResolverFactory = new DatabaseChangeResolverFactory(dialogService, originalDatabase, preferences);
@@ -159,6 +166,7 @@ private static Optional showReviewBackupDialog(
}
// In case not all changes are resolved, start from scratch
+ BackupManagerGit.writeBackupFileToCommit(originalPath, backupPath, latestCommitId);
return showRestoreBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager);
});
} catch (IOException e) {
From b1c006e69ba258748b2787d5d6b832179fdc36a3 Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Wed, 4 Dec 2024 23:03:04 +0100
Subject: [PATCH 64/84] Remove usage of BackupFileUtil in UI
---
.../gui/backup/BackupResolverDialog.java | 38 +------------------
.../jabref/gui/dialogs/BackupUIManager.java | 18 +++++----
2 files changed, 12 insertions(+), 44 deletions(-)
diff --git a/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
index c7aad7811ba..aab94b961b3 100644
--- a/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
+++ b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
@@ -1,23 +1,12 @@
package org.jabref.gui.backup;
-import java.io.IOException;
import java.nio.file.Path;
-import java.util.Optional;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
-import javafx.scene.control.Hyperlink;
import org.jabref.gui.FXDialog;
-import org.jabref.gui.desktop.os.NativeDesktop;
-import org.jabref.gui.frame.ExternalApplicationsPreferences;
import org.jabref.logic.l10n.Localization;
-import org.jabref.logic.util.BackupFileType;
-import org.jabref.logic.util.io.BackupFileUtil;
-
-import org.controlsfx.control.HyperlinkLabel;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
public class BackupResolverDialog extends FXDialog {
public static final ButtonType RESTORE_FROM_BACKUP = new ButtonType(Localization.lang("Restore from latest backup"), ButtonBar.ButtonData.OK_DONE);
@@ -25,38 +14,15 @@ public class BackupResolverDialog extends FXDialog {
public static final ButtonType IGNORE_BACKUP = new ButtonType(Localization.lang("Ignore backup"), ButtonBar.ButtonData.CANCEL_CLOSE);
public static final ButtonType COMPARE_OLDER_BACKUP = new ButtonType("Compare older backup", ButtonBar.ButtonData.LEFT);
- private static final Logger LOGGER = LoggerFactory.getLogger(BackupResolverDialog.class);
-
- public BackupResolverDialog(Path originalPath, Path backupDir, ExternalApplicationsPreferences externalApplicationsPreferences) {
+ public BackupResolverDialog(Path originalPath) {
super(AlertType.CONFIRMATION, Localization.lang("Backup found"), true);
setHeaderText(null);
getDialogPane().setMinHeight(180);
getDialogPane().getButtonTypes().setAll(RESTORE_FROM_BACKUP, REVIEW_BACKUP, IGNORE_BACKUP, COMPARE_OLDER_BACKUP);
- Optional backupPathOpt = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
- String backupFilename = backupPathOpt.map(Path::getFileName).map(Path::toString).orElse(Localization.lang("File not found"));
- String content = Localization.lang("A backup file for '%0' was found at [%1]", originalPath.getFileName().toString(), backupFilename) + "\n" +
+ String content = Localization.lang("A backup file for '%0' was found.", originalPath.getFileName().toString()) + "\n" +
Localization.lang("This could indicate that JabRef did not shut down cleanly last time the file was used.") + "\n\n" +
Localization.lang("Do you want to recover the library from the backup file?");
setContentText(content);
-
- HyperlinkLabel contentLabel = new HyperlinkLabel(content);
- contentLabel.setPrefWidth(360);
- contentLabel.setOnAction(e -> {
- if (backupPathOpt.isPresent()) {
- if (!(e.getSource() instanceof Hyperlink)) {
- return;
- }
- String clickedLinkText = ((Hyperlink) (e.getSource())).getText();
- if (backupFilename.equals(clickedLinkText)) {
- try {
- NativeDesktop.openFolderAndSelectFile(backupPathOpt.get(), externalApplicationsPreferences, null);
- } catch (IOException ex) {
- LOGGER.error("Could not open backup folder", ex);
- }
- }
- }
- });
- getDialogPane().setContent(contentLabel);
}
}
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 8c7333ebf83..652e152470b 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -21,7 +21,6 @@
import org.jabref.gui.collab.DatabaseChangeList;
import org.jabref.gui.collab.DatabaseChangeResolverFactory;
import org.jabref.gui.collab.DatabaseChangesResolverDialog;
-import org.jabref.gui.frame.ExternalApplicationsPreferences;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.gui.undo.NamedCompound;
import org.jabref.gui.util.UiTaskExecutor;
@@ -57,26 +56,31 @@ public static Optional showRestoreBackupDialog(DialogService dialo
LOGGER.info("Show restore backup dialog");
var actionOpt = showBackupResolverDialog(
dialogService,
- preferences.getExternalApplicationsPreferences(),
- originalPath,
- preferences.getFilePreferences().getBackupDirectory());
+ originalPath);
return actionOpt.flatMap(action -> {
try {
+
List commits = BackupManagerGit.retrieveCommits(originalPath, preferences.getFilePreferences().getBackupDirectory(), -1);
List backups = BackupManagerGit.retrieveCommitDetails(commits, originalPath, preferences.getFilePreferences().getBackupDirectory()).reversed();
+
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
ObjectId commitId = backups.getFirst().getId();
+
BackupManagerGit.restoreBackup(originalPath, preferences.getFilePreferences().getBackupDirectory(), commitId);
+
return Optional.empty();
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
ObjectId commitId = backups.getFirst().getId();
+
return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager, commitId, commitId);
} else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
var recordBackupChoice = showBackupChoiceDialog(dialogService, originalPath, preferences, backups);
+
if (recordBackupChoice.isEmpty()) {
return Optional.empty();
}
+
if (recordBackupChoice.get().action() == BackupChoiceDialog.RESTORE_BACKUP) {
LOGGER.warn(recordBackupChoice.get().entry().getSize());
ObjectId commitId = recordBackupChoice.get().entry().getId();
@@ -99,11 +103,9 @@ public static Optional showRestoreBackupDialog(DialogService dialo
}
private static Optional showBackupResolverDialog(DialogService dialogService,
- ExternalApplicationsPreferences externalApplicationsPreferences,
- Path originalPath,
- Path backupDir) {
+ Path originalPath) {
return UiTaskExecutor.runInJavaFXThread(
- () -> dialogService.showCustomDialogAndWait(new BackupResolverDialog(originalPath, backupDir, externalApplicationsPreferences)));
+ () -> dialogService.showCustomDialogAndWait(new BackupResolverDialog(originalPath)));
}
private static Optional showBackupChoiceDialog(DialogService dialogService,
From aac491526667b667f942399d9375da91273ccd3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 00:31:19 +0100
Subject: [PATCH 65/84] BackupResolverDialog.java doesn't use the old backup
system anymore.
---
src/main/java/org/jabref/gui/backup/BackupResolverDialog.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
index aab94b961b3..750e94a9e4f 100644
--- a/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
+++ b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
@@ -20,7 +20,7 @@ public BackupResolverDialog(Path originalPath) {
getDialogPane().setMinHeight(180);
getDialogPane().getButtonTypes().setAll(RESTORE_FROM_BACKUP, REVIEW_BACKUP, IGNORE_BACKUP, COMPARE_OLDER_BACKUP);
- String content = Localization.lang("A backup file for '%0' was found.", originalPath.getFileName().toString()) + "\n" +
+ String content = Localization.lang("A backup for '%0' was found.", originalPath.getFileName().toString()) + "\n" +
Localization.lang("This could indicate that JabRef did not shut down cleanly last time the file was used.") + "\n\n" +
Localization.lang("Do you want to recover the library from the backup file?");
setContentText(content);
From a292db35e95af7b788b2cf10bae90107d97bd320 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 01:39:46 +0100
Subject: [PATCH 66/84] change in CHANGELOG.md
---
CHANGELOG.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 38e1c8b26c9..581b1a92a52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,11 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
### Added
+- Implemented BackupManagerGit to handle automatic backups of .bib files using Git, ensuring centralized, version-controlled backup management. [#2961] (https://github.com/JabRef/jabref/issues/2961#event-14646591632)
+- Added automatic copying of .bib files to a jabref/backups directory every 19 seconds with comprehensive commit history. [#2961]
+- Added support for unique file naming using UUIDs to prevent overwriting files with identical names in the backup directory. [#2961]
+- Introduced UI functionality (only during opening) for saving, restoring, reviewing and discarding changes with accurate commit details retrieval. [#2961]
+- Enabled a "Restore" button to recover specific backup versions. [#2961]
- We added a "view as BibTeX" option before importing an entry from the citation relation tab. [#11826](https://github.com/JabRef/jabref/issues/11826)
- We added support finding LaTeX-encoded special characters based on plain Unicode and vice versa. [#11542](https://github.com/JabRef/jabref/pull/11542)
- When a search hits a file, the file icon of that entry is changed accordingly. [#11542](https://github.com/JabRef/jabref/pull/11542)
@@ -47,6 +52,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
### Changed
+- Refactored backup-related components to integrate with the new BackupManagerGit, replacing the older BackupManager logic. [#2961]
- A search in "any" fields ignores the [groups](https://docs.jabref.org/finding-sorting-and-cleaning-entries/groups). [#7996](https://github.com/JabRef/jabref/issues/7996)
- When a communication error with an [online service](https://docs.jabref.org/collect/import-using-online-bibliographic-database) occurs, JabRef displays the HTTP error. [#11223](https://github.com/JabRef/jabref/issues/11223)
- The Pubmed/Medline Plain importer now imports the PMID field as well [#11488](https://github.com/JabRef/jabref/issues/11488)
From 7ae97d1317d630167720a8dc0341ecd17bcf469c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 11:54:56 +0100
Subject: [PATCH 67/84] Updated submodule csl-locales to latest commit
---
src/main/resources/csl-locales | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/resources/csl-locales b/src/main/resources/csl-locales
index 8bc2af16f51..4753e3a9aca 160000
--- a/src/main/resources/csl-locales
+++ b/src/main/resources/csl-locales
@@ -1 +1 @@
-Subproject commit 8bc2af16f5180a8e4fb591c2be916650f75bb8f6
+Subproject commit 4753e3a9aca4b806ac0e3036ed727d47bf8f678e
From 70b123bdf51a7c3f3bcdbe7754627ea6ff764b96 Mon Sep 17 00:00:00 2001
From: Khaoula AROUISSI <66998680+khola22@users.noreply.github.com>
Date: Thu, 5 Dec 2024 16:14:52 +0100
Subject: [PATCH 68/84] Update CHANGELOG.md
Co-authored-by: Subhramit Basu Bhowmick
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a3cc22ecc56..87131f39990 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,7 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
### Added
-- Implemented BackupManagerGit to handle automatic backups of .bib files using Git, ensuring centralized, version-controlled backup management. [#2961](https://github.com/JabRef/jabref/issues/2961)
+- Implemented version-control based backup management of .bib files using Git. [#2961](https://github.com/JabRef/jabref/issues/2961)
- Added automatic copying of .bib files to a jabref/backups directory every 19 seconds with comprehensive commit history. [#2961](https://github.com/JabRef/jabref/issues/2961)
- Added support for unique file naming using UUIDs to prevent overwriting files with identical names in the backup directory. [#2961](https://github.com/JabRef/jabref/issues/2961)
- Introduced UI functionality (only during opening) for saving, restoring, reviewing and discarding changes with accurate commit details retrieval. [#2961](https://github.com/JabRef/jabref/issues/2961)
From 69ad0f335c2f932c981d7127dc49966ddaa2bb4f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 16:22:22 +0100
Subject: [PATCH 69/84] CHANGELOG.md changes
---
CHANGELOG.md | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87131f39990..b017558a542 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,11 +11,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
### Added
-- Implemented version-control based backup management of .bib files using Git. [#2961](https://github.com/JabRef/jabref/issues/2961)
-- Added automatic copying of .bib files to a jabref/backups directory every 19 seconds with comprehensive commit history. [#2961](https://github.com/JabRef/jabref/issues/2961)
-- Added support for unique file naming using UUIDs to prevent overwriting files with identical names in the backup directory. [#2961](https://github.com/JabRef/jabref/issues/2961)
-- Introduced UI functionality (only during opening) for saving, restoring, reviewing and discarding changes with accurate commit details retrieval. [#2961](https://github.com/JabRef/jabref/issues/2961)
-- Enabled a "Restore" button to recover specific backup versions. [#2961](https://github.com/JabRef/jabref/issues/2961)
+- Enhanced backup and restore functionality. [#2961](https://github.com/JabRef/jabref/issues/2961)
- We added a Markdown export layout. [#12220](https://github.com/JabRef/jabref/pull/12220)
- We added a "view as BibTeX" option before importing an entry from the citation relation tab. [#11826](https://github.com/JabRef/jabref/issues/11826)
- We added support finding LaTeX-encoded special characters based on plain Unicode and vice versa. [#11542](https://github.com/JabRef/jabref/pull/11542)
@@ -57,7 +53,6 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
### Changed
-- Refactored backup-related components to integrate with the new BackupManagerGit, replacing the older BackupManager logic. [#2961](https://github.com/JabRef/jabref/issues/2961)
- A search in "any" fields ignores the [groups](https://docs.jabref.org/finding-sorting-and-cleaning-entries/groups). [#7996](https://github.com/JabRef/jabref/issues/7996)
- When a communication error with an [online service](https://docs.jabref.org/collect/import-using-online-bibliographic-database) occurs, JabRef displays the HTTP error. [#11223](https://github.com/JabRef/jabref/issues/11223)
- The Pubmed/Medline Plain importer now imports the PMID field as well [#11488](https://github.com/JabRef/jabref/issues/11488)
From a577ad9b34b7d5983e34afd2add204cec0c4c981 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 16:30:50 +0100
Subject: [PATCH 70/84] Resolve comment on LibraryTab.java and
BackupManagerGit.java
---
src/main/java/org/jabref/gui/LibraryTab.java | 2 --
.../autosaveandbackup/BackupManagerGit.java | 34 ++++++++-----------
2 files changed, 14 insertions(+), 22 deletions(-)
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index 6ebbceda379..99a2986be6c 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -1083,8 +1083,6 @@ public static LibraryTab createLibraryTab(BackgroundTask dataLoadi
try {
newTab.onDatabaseLoadingSucceed(result);
} catch (Exception e) {
- // We need to handle the exception.
- // Handle the exception, e.g., log it or show an error dialog
LOGGER.error("An error occurred while loading the database", e);
}
})
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 9dbcc0d60b4..aa20123883a 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -55,7 +55,7 @@ public class BackupManagerGit {
private static Git git;
private final BibDatabaseContext bibDatabaseContext;
- private final CliPreferences preferences;
+ private final Path backupDirectory;
private final ScheduledThreadPoolExecutor executor;
private final CoarseChangeFilter changeFilter;
private final BibEntryTypesManager entryTypesManager;
@@ -63,7 +63,7 @@ public class BackupManagerGit {
private boolean needsBackup = false;
- BackupManagerGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
+ BackupManagerGit(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, Path backupDir) throws IOException, GitAPIException {
Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
if (!Files.exists(dbFile)) {
LOGGER.error("Database file does not exist: {}", dbFile);
@@ -73,24 +73,24 @@ public class BackupManagerGit {
this.bibDatabaseContext = bibDatabaseContext;
LOGGER.info("Backup manager initialized for file: {}", bibDatabaseContext.getDatabasePath().orElseThrow());
this.entryTypesManager = entryTypesManager;
- this.preferences = preferences;
+ this.backupDirectory = backupDir;
this.executor = new ScheduledThreadPoolExecutor(2);
this.libraryTab = libraryTab;
changeFilter = new CoarseChangeFilter(bibDatabaseContext);
changeFilter.registerListener(this);
- LOGGER.info("Backup directory path: {}", preferences.getFilePreferences().getBackupDirectory());
+ LOGGER.info("Backup directory path: {}", backupDirectory);
- ensureGitInitialized(preferences.getFilePreferences().getBackupDirectory());
+ ensureGitInitialized(backupDirectory);
- File backupDirFile = preferences.getFilePreferences().getBackupDirectory().toFile();
+ File backupDirFile = backupDirectory.toFile();
if (!backupDirFile.exists() && !backupDirFile.mkdirs()) {
- LOGGER.error("Failed to create backup directory: {}", preferences.getFilePreferences().getBackupDirectory());
- throw new IOException("Unable to create backup directory: " + preferences.getFilePreferences().getBackupDirectory());
+ LOGGER.error("Failed to create backup directory: {}", backupDirectory);
+ throw new IOException("Unable to create backup directory: " + backupDirectory);
}
- copyDatabaseFileToBackupDir(dbFile, preferences.getFilePreferences().getBackupDirectory());
+ copyDatabaseFileToBackupDir(dbFile, backupDirectory);
}
/**
@@ -232,7 +232,8 @@ private static void copyDatabaseFileToBackupDir(Path dbFile, Path backupDirPath)
public static BackupManagerGit start(LibraryTab libraryTab, BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, CliPreferences preferences) throws IOException, GitAPIException {
LOGGER.info("In methode Start");
- BackupManagerGit backupManagerGit = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, preferences);
+ Path backupDir = preferences.getFilePreferences().getBackupDirectory();
+ BackupManagerGit backupManagerGit = new BackupManagerGit(libraryTab, bibDatabaseContext, entryTypesManager, backupDir);
backupManagerGit.startBackupTask(preferences.getFilePreferences().getBackupDirectory(), bibDatabaseContext);
runningInstances.add(backupManagerGit);
return backupManagerGit;
@@ -354,10 +355,7 @@ public static void restoreBackup(Path dbFile, Path backupDir, ObjectId objectId)
// Rewrite the original file at dbFile path
rewriteFile(dbFile, fileContent);
LOGGER.info("Restored content to: {}", dbFile);
- } catch (
- IOException |
- IllegalArgumentException |
- GitAPIException e) {
+ } catch (IOException | IllegalArgumentException | GitAPIException e) {
LOGGER.error("Error while restoring the backup: {}", e.getMessage(), e);
}
}
@@ -435,8 +433,7 @@ public static Path getBackupFilePath(Path dbFile, Path backupDir) {
String uuid = getOrGenerateFileUuid(dbFile);
String relativeFileName = baseName.replace(".bib", "") + "_" + uuid + ".bib";
return backupDir.resolve(relativeFileName);
- } catch (
- IOException e) {
+ } catch (IOException e) {
throw new RuntimeException(e);
}
}
@@ -466,10 +463,7 @@ public static void writeBackupFileToCommit(Path dbFile, Path backupDir, ObjectId
// Rewrite the original file at backupFilePath path
rewriteFile(backupFilePath, fileContent);
LOGGER.info("Restored content to: {}", dbFile);
- } catch (
- IOException |
- IllegalArgumentException |
- GitAPIException e) {
+ } catch (IOException | IllegalArgumentException | GitAPIException e) {
LOGGER.error("Error while restoring the backup: {}", e.getMessage(), e);
}
}
From e55a831377cb666d334d840eeec21f89c2155acd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 16:36:42 +0100
Subject: [PATCH 71/84] Resolve comment on LibraryTab.java and
BackupManagerGit.java
---
.../gui/autosaveandbackup/BackupManagerGit.java | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index aa20123883a..b45276eb442 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -48,6 +48,7 @@ public class BackupManagerGit {
static Set runningInstances = new HashSet();
+ private static final String LINE_BREAK = System.lineSeparator();
private static final Logger LOGGER = LoggerFactory.getLogger(BackupManagerGit.class);
private static final int DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS = 19;
@@ -162,14 +163,14 @@ private static String normalizeBibTeX(String input) {
return "";
}
- // Diviser les lignes et traiter chaque ligne
+ // Split lines and process each line
Stream lines = input.lines();
- // Normalisation des lignes
+ // Normalize lines
String normalized = lines
- .map(String::trim) // Supprimer les espaces en début et fin de ligne
- .filter(line -> !line.isBlank()) // Supprimer les lignes vides
- .collect(Collectors.joining("\n")); // Réassembler avec des sauts de ligne
+ .map(String::trim) // Remove leading and trailing spaces
+ .filter(line -> !line.isBlank()) // Remove blank lines
+ .collect(Collectors.joining(LINE_BREAK)); // Reassemble with line breaks
return normalized;
}
From cf23e682ee5cf59ee7c253bc83ad09e7f3d4531f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 16:43:03 +0100
Subject: [PATCH 72/84] Resolve comment on BackupUIManager.java
---
src/main/java/org/jabref/gui/dialogs/BackupUIManager.java | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 652e152470b..58a124e611b 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -75,7 +75,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager, commitId, commitId);
} else if (action == BackupResolverDialog.COMPARE_OLDER_BACKUP) {
- var recordBackupChoice = showBackupChoiceDialog(dialogService, originalPath, preferences, backups);
+ var recordBackupChoice = showBackupChoiceDialog(dialogService, preferences, backups);
if (recordBackupChoice.isEmpty()) {
return Optional.empty();
@@ -94,8 +94,7 @@ public static Optional showRestoreBackupDialog(DialogService dialo
return showReviewBackupDialog(dialogService, originalPath, preferences, fileUpdateMonitor, undoManager, stateManager, commitId, latestCommitId);
}
}
- } catch (
- GitAPIException | IOException e) {
+ } catch (GitAPIException | IOException e) {
throw new RuntimeException(e);
}
return Optional.empty();
@@ -109,7 +108,6 @@ private static Optional showBackupResolverDialog(DialogService dialo
}
private static Optional showBackupChoiceDialog(DialogService dialogService,
- Path originalPath,
GuiPreferences preferences,
List backups) {
return UiTaskExecutor.runInJavaFXThread(
From 61a1987d0170f055c13df6b03488892010f34126 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 16:50:19 +0100
Subject: [PATCH 73/84] checkstyle
---
src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index 727fc394eff..f55ec43bd0d 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -215,9 +215,7 @@ private boolean save(BibDatabaseContext bibDatabaseContext, SaveDatabaseMode mod
return savePath.filter(path -> {
try {
return saveAs(path, mode);
- } catch (
- GitAPIException |
- IOException e) {
+ } catch (GitAPIException | IOException e) {
LOGGER.error("A problem occurred when trying to save the file %s".formatted(path), e);
throw new RuntimeException(e);
}
From b515e5176aeef8a0d6446cdecaac443e06c84d4f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 18:53:32 +0100
Subject: [PATCH 74/84] Working Tests
---
.../autosaveandbackup/BackupManagerGit.java | 16 ++++---
.../BackupManagerGitTest.java | 42 ++++++++++++++-----
2 files changed, 38 insertions(+), 20 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index b45276eb442..884eb953724 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -122,7 +122,7 @@ private static String appendUuidToFileName(String originalFileName, String uuid)
* @return The UUID associated with the file.
* @throws IOException If an error occurs while accessing or creating the UUID.
*/
- private static String getOrGenerateFileUuid(Path filePath) throws IOException {
+ protected static String getOrGenerateFileUuid(Path filePath) throws IOException {
// Define a hidden metadata file to store the UUID
Path metadataFile = filePath.resolveSibling("." + filePath.getFileName().toString() + ".uuid");
@@ -205,16 +205,12 @@ static void ensureGitInitialized(Path backupDir) throws IOException, GitAPIExcep
}
// Helper method to copy the database file to the backup directory
- private static void copyDatabaseFileToBackupDir(Path dbFile, Path backupDirPath) throws IOException {
+ protected static void copyDatabaseFileToBackupDir(Path dbFile, Path backupDirPath) throws IOException {
String fileUuid = getOrGenerateFileUuid(dbFile);
String uniqueFileName = appendUuidToFileName(dbFile.getFileName().toString(), fileUuid);
Path backupFilePath = backupDirPath.resolve(uniqueFileName);
- if (!Files.exists(backupFilePath) || Files.mismatch(dbFile, backupFilePath) != -1) {
- Files.copy(dbFile, backupFilePath, StandardCopyOption.REPLACE_EXISTING);
- LOGGER.info("Database file uniquely copied to backup directory: {}", backupFilePath);
- } else {
- LOGGER.info("No changes detected; skipping backup for file: {}", uniqueFileName);
- }
+ Files.copy(dbFile, backupFilePath, StandardCopyOption.REPLACE_EXISTING);
+ LOGGER.info("Database file uniquely copied to backup directory: {}", backupFilePath);
}
// A method
@@ -641,7 +637,7 @@ public static void performBackupNoCommits(Path dbFile, Path backupDir) throws IO
copyDatabaseFileToBackupDir(dbFile, backupDir);
// Ensure the Git repository exists
- LOGGER.info("Checking if backup differs for file: {}", dbFile);
+ LOGGER.info("Ensuring the .git is initialized");
ensureGitInitialized(backupDir);
// Open the Git repository located in the backup directory
@@ -653,9 +649,11 @@ public static void performBackupNoCommits(Path dbFile, Path backupDir) throws IO
String repoFileName = baseName.replace(".bib", "") + "_" + uuid + ".bib";
// Stage the file for commit
+ LOGGER.info("Staging the file for commit");
git.add().addFilepattern(repoFileName).call();
// Commit the staged changes
+ LOGGER.info("Committing the file");
RevCommit commit = git.commit()
.setMessage("Backup at " + Instant.now().toString())
.call();
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 9f5792634a4..54ed90cfec8 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -20,6 +20,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
@@ -100,8 +101,8 @@ void tearDown() throws IOException {
@Test
void testInitializationCreatesBackupDirectory() throws IOException, GitAPIException {
// Create BackupManagerGit
- BackupManagerGit manager1 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, mockPreferences);
- BackupManagerGit manager2 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext2, mockEntryTypesManager, mockPreferences);
+ BackupManagerGit manager1 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, tempDir);
+ BackupManagerGit manager2 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext2, mockEntryTypesManager, tempDir);
// Check if the backup directory exists
assertTrue(Files.exists(tempDir), " directory should be created wich contains .git and single copies og .bib");
assertTrue(Files.exists(tempDir1), "Backup directory should be created during initialization.");
@@ -120,15 +121,21 @@ void testGitInitialization() throws IOException, GitAPIException {
@Test
void testBackupFileCopiedToDirectory() throws IOException, GitAPIException {
+ BackupManagerGit manager1 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, tempDir);
+ BackupManagerGit manager2 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext2, mockEntryTypesManager, tempDir);
- BackupManagerGit manager1 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, mockPreferences);
- BackupManagerGit manager2 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext2, mockEntryTypesManager, mockPreferences);
+ // Generate the expected backup file names
+ String uuid1 = BackupManagerGit.getOrGenerateFileUuid(mockDatabasePath1);
+ String uuid2 = BackupManagerGit.getOrGenerateFileUuid(mockDatabasePath2);
+ String backupFileName1 = mockDatabasePath1.getFileName().toString().replace(".bib", "") + "_" + uuid1 + ".bib";
+ String backupFileName2 = mockDatabasePath2.getFileName().toString().replace(".bib", "") + "_" + uuid2 + ".bib";
// Verify the file is copied to the backup directory
- Path backupFile1 = tempDir.resolve(this.mockDatabasePath1.getFileName());
- Path backupFile2 = tempDir.resolve(this.mockDatabasePath2.getFileName());
+ Path backupFile1 = tempDir.resolve(backupFileName1);
+ Path backupFile2 = tempDir.resolve(backupFileName2);
assertTrue(Files.exists(backupFile1), "Database file should be copied to the backup directory.");
- }
+ assertTrue(Files.exists(backupFile2), "Database file should be copied to the backup directory.");
+ }
@Test
public void testStart() throws IOException, GitAPIException {
@@ -143,15 +150,28 @@ void testPerformBackupCommitsChanges() throws IOException, GitAPIException {
// Create a test file
Path dbFile1 = tempDir.resolve("test1.bib");
- Files.writeString(dbFile1, "Initial content of test 1");
// Create BackupManagerGit and perform backup
- BackupManagerGit manager = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, mockPreferences);
- manager.performBackup(tempDir);
+ BackupManagerGit manager = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, tempDir);
+ Files.writeString(dbFile1, "Initial content of test 1");
+
+ BackupManagerGit.copyDatabaseFileToBackupDir(dbFile1, tempDir);
+
+ // Generate the expected backup file name
+ String uuid1 = BackupManagerGit.getOrGenerateFileUuid(dbFile1);
+ String backupFileName1 = dbFile1.getFileName().toString().replace(".bib", "") + "_" + uuid1 + ".bib";
+ Path backupFile1 = tempDir.resolve(backupFileName1);
+
+ // Verify the file is copied to the backup directory
+ assertTrue(Files.exists(backupFile1), "Database file should be copied to the backup directory.");
+
+ manager.performBackup(dbFile1, tempDir);
// Verify that changes are committed
try (Git git = Git.open(tempDir.toFile())) {
- assertTrue(git.status().call().isClean(), "Git repository should have no uncommitted changes after backup.");
+ boolean hasUncommittedChanges = git.status().call().getUncommittedChanges().stream()
+ .anyMatch(file -> file.endsWith(".bib"));
+ assertFalse(hasUncommittedChanges, "Git repository should have no uncommitted .bib file changes after backup.");
}
}
}
From 8f06c8091aa3b7714a234dae2f2d5c510167862a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 19:00:12 +0100
Subject: [PATCH 75/84] Add missing submodule configuration
---
.gitmodules | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.gitmodules b/.gitmodules
index 10944a3d5a0..0c5a1380408 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,3 +13,6 @@
url = https://github.com/citation-style-language/locales.git
ignore = all
shallow = true
+[submodule "jabref"]
+ path = jabref
+ url = https://github.com/JabRef/jabref.git
From d3b6dd3e9f49680632ba10a1a398bd99f865d783 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 19:12:50 +0100
Subject: [PATCH 76/84] Apply OpenRewrite recipes
---
.../gui/autosaveandbackup/BackupManagerGit.java | 17 ++++++++---------
src/main/resources/csl-locales | 2 +-
src/main/resources/csl-styles | 2 +-
.../autosaveandbackup/BackupManagerGitTest.java | 15 ++++++++-------
4 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 884eb953724..115293072aa 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -46,7 +46,7 @@
public class BackupManagerGit {
- static Set runningInstances = new HashSet();
+ static Set runningInstances = new HashSet<>();
private static final String LINE_BREAK = System.lineSeparator();
private static final Logger LOGGER = LoggerFactory.getLogger(BackupManagerGit.class);
@@ -292,14 +292,14 @@ protected void performBackup(Path dbfile, Path backupDir) throws IOException, Gi
boolean needsCommit = backupGitDiffers(dbfile, backupDir);
if (!needsBackup && !needsCommit) {
- LOGGER.info("No changes detected, beacuse needsBackup is :" + needsBackup + " and needsCommit is :" + needsCommit);
+ LOGGER.info("No changes detected, beacuse needsBackup is :{} and needsCommit is :{}", needsBackup, needsCommit);
return;
}
if (needsBackup) {
- LOGGER.info("Backup needed, because needsBackup is :" + needsBackup);
+ LOGGER.info("Backup needed, because needsBackup is :{}", needsBackup);
} else {
- LOGGER.info("Backup needed, because needsCommit is :" + needsCommit);
+ LOGGER.info("Backup needed, because needsCommit is :{}", needsCommit);
}
// Stage the file for commit
@@ -310,8 +310,7 @@ protected void performBackup(Path dbfile, Path backupDir) throws IOException, Gi
RevCommit commit = git.commit()
.setMessage("Backup at " + Instant.now().toString())
.call();
- LOGGER.info("Backup committed in :" + backupDir + " with commit ID: " + commit.getName()
- + " for the file : {}", bibDatabaseContext.getDatabasePath().orElseThrow());
+ LOGGER.info("Backup committed in :{} with commit ID: {} for the file : {}", backupDir, commit.getName(), bibDatabaseContext.getDatabasePath().orElseThrow());
}
public synchronized void listen(BibDatabaseContextChangedEvent event) {
@@ -604,9 +603,9 @@ public static List retrieveCommitDetails(List commits, P
}
// Convert size to KB or MB
- sizeFormatted = (fileSize > 1024 * 1024)
- ? String.format("%.2f MB", fileSize / (1024.0 * 1024.0))
- : String.format("%.2f KB", fileSize / 1024.0);
+ sizeFormatted = fileSize > 1024 * 1024
+ ? "%.2f MB".formatted(fileSize / (1024.0 * 1024.0))
+ : "%.2f KB".formatted(fileSize / 1024.0);
}
// Skip this commit if the file was not found
diff --git a/src/main/resources/csl-locales b/src/main/resources/csl-locales
index 96d704de2fc..4753e3a9aca 160000
--- a/src/main/resources/csl-locales
+++ b/src/main/resources/csl-locales
@@ -1 +1 @@
-Subproject commit 96d704de2fc7b930ae4a0ec4686a7143bb4a0d33
+Subproject commit 4753e3a9aca4b806ac0e3036ed727d47bf8f678e
diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles
index 6b7b611908b..49af15c4f5b 160000
--- a/src/main/resources/csl-styles
+++ b/src/main/resources/csl-styles
@@ -1 +1 @@
-Subproject commit 6b7b611908b20c91f34110d1c9489fb3278e0ef5
+Subproject commit 49af15c4f5bca025b6b18ca48c447016586f01e7
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index 54ed90cfec8..dbd9022dd67 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -4,6 +4,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Optional;
import org.jabref.gui.LibraryTab;
import org.jabref.logic.FilePreferences;
@@ -66,8 +67,8 @@ public void setUp(@TempDir Path tempDir) throws IOException, GitAPIException {
Files.writeString(mockDatabasePath1, "Mock content for testing 1"); // Create the file
Files.writeString(mockDatabasePath2, "Mock content for testing 2"); // Create the file
- when(mockDatabaseContext1.getDatabasePath()).thenReturn(java.util.Optional.of(mockDatabasePath1));
- when(mockDatabaseContext2.getDatabasePath()).thenReturn(java.util.Optional.of(mockDatabasePath2));
+ when(mockDatabaseContext1.getDatabasePath()).thenReturn(Optional.of(mockDatabasePath1));
+ when(mockDatabaseContext2.getDatabasePath()).thenReturn(Optional.of(mockDatabasePath2));
when(filePreferences.getBackupDirectory()).thenReturn(tempDir);
@@ -99,7 +100,7 @@ void tearDown() throws IOException {
}
@Test
- void testInitializationCreatesBackupDirectory() throws IOException, GitAPIException {
+ void initializationCreatesBackupDirectory() throws IOException, GitAPIException {
// Create BackupManagerGit
BackupManagerGit manager1 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, tempDir);
BackupManagerGit manager2 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext2, mockEntryTypesManager, tempDir);
@@ -110,7 +111,7 @@ void testInitializationCreatesBackupDirectory() throws IOException, GitAPIExcept
}
@Test
- void testGitInitialization() throws IOException, GitAPIException {
+ void gitInitialization() throws IOException, GitAPIException {
// Initialize Git
BackupManagerGit.ensureGitInitialized(tempDir);
@@ -120,7 +121,7 @@ void testGitInitialization() throws IOException, GitAPIException {
}
@Test
- void testBackupFileCopiedToDirectory() throws IOException, GitAPIException {
+ void backupFileCopiedToDirectory() throws IOException, GitAPIException {
BackupManagerGit manager1 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, tempDir);
BackupManagerGit manager2 = new BackupManagerGit(mockLibraryTab, mockDatabaseContext2, mockEntryTypesManager, tempDir);
@@ -138,13 +139,13 @@ void testBackupFileCopiedToDirectory() throws IOException, GitAPIException {
}
@Test
- public void testStart() throws IOException, GitAPIException {
+ public void start() throws IOException, GitAPIException {
BackupManagerGit startedManager = BackupManagerGit.start(mockLibraryTab, mockDatabaseContext1, mockEntryTypesManager, mockPreferences);
assertNotNull(startedManager);
}
@Test
- void testPerformBackupCommitsChanges() throws IOException, GitAPIException {
+ void performBackupCommitsChanges() throws IOException, GitAPIException {
// Initialize Git
BackupManagerGit.ensureGitInitialized(tempDir);
From 2a05da51c6ed9fc1dcdea44c0311583346db2584 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Thu, 5 Dec 2024 22:53:59 +0100
Subject: [PATCH 77/84] Correct Unit Tests
---
src/test/java/org/jabref/logic/bst/BstVMVisitorTest.java | 8 ++++----
.../jabref/logic/importer/fileformat/CffImporterTest.java | 8 ++++----
.../logic/util/io/CitationKeyBasedFileFinderTest.java | 4 ++--
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/test/java/org/jabref/logic/bst/BstVMVisitorTest.java b/src/test/java/org/jabref/logic/bst/BstVMVisitorTest.java
index 23c0f7e0eed..c8c57f250db 100644
--- a/src/test/java/org/jabref/logic/bst/BstVMVisitorTest.java
+++ b/src/test/java/org/jabref/logic/bst/BstVMVisitorTest.java
@@ -203,8 +203,8 @@ void visitIdentifier() {
FUNCTION { test } {
#1 'local.variable :=
#2 'variable :=
- "COMPARE_OLDER_BACKUP" 'local.label :=
- "COMPARE_OLDER_BACKUP-GLOBAL" 'label :=
+ "TEST" 'local.label :=
+ "TEST-GLOBAL" 'label :=
local.label local.variable
label variable
}
@@ -215,9 +215,9 @@ void visitIdentifier() {
vm.render(testEntries);
assertEquals(2, vm.getStack().pop());
- assertEquals("COMPARE_OLDER_BACKUP-GLOBAL", vm.getStack().pop());
+ assertEquals("TEST-GLOBAL", vm.getStack().pop());
assertEquals(1, vm.getStack().pop());
- assertEquals("COMPARE_OLDER_BACKUP", vm.getStack().pop());
+ assertEquals("TEST", vm.getStack().pop());
assertEquals(0, vm.getStack().size());
}
diff --git a/src/test/java/org/jabref/logic/importer/fileformat/CffImporterTest.java b/src/test/java/org/jabref/logic/importer/fileformat/CffImporterTest.java
index bca58a77401..195d7bd713b 100644
--- a/src/test/java/org/jabref/logic/importer/fileformat/CffImporterTest.java
+++ b/src/test/java/org/jabref/logic/importer/fileformat/CffImporterTest.java
@@ -175,7 +175,7 @@ void importEntriesPreferredCitation() throws IOException, URISyntaxException {
BibEntry expectedPreferred = new BibEntry(StandardEntryType.InProceedings)
.withCitationKey(citeKey)
.withField(StandardField.AUTHOR, "Jonathan von Duke and Jim Kingston, Jr.")
- .withField(StandardField.DOI, "10.0001/COMPARE_OLDER_BACKUP")
+ .withField(StandardField.DOI, "10.0001/TEST")
.withField(StandardField.URL, "www.github.com");
assertEquals(mainEntry, expectedMain);
@@ -198,13 +198,13 @@ void importEntriesReferences() throws IOException, URISyntaxException {
.withCitationKey(citeKey1)
.withField(StandardField.AUTHOR, "Jonathan von Duke and Jim Kingston, Jr.")
.withField(StandardField.YEAR, "2007")
- .withField(StandardField.DOI, "10.0001/COMPARE_OLDER_BACKUP")
+ .withField(StandardField.DOI, "10.0001/TEST")
.withField(StandardField.URL, "www.example.com");
BibEntry expectedReference2 = new BibEntry(StandardEntryType.Manual)
.withCitationKey(citeKey2)
.withField(StandardField.AUTHOR, "Arthur Clark, Jr. and Luca von Diamond")
- .withField(StandardField.DOI, "10.0002/COMPARE_OLDER_BACKUP")
+ .withField(StandardField.DOI, "10.0002/TEST")
.withField(StandardField.URL, "www.facebook.com");
assertEquals(mainEntry, expectedMain);
@@ -218,7 +218,7 @@ public BibEntry getPopulatedEntry() {
.withField(StandardField.TITLE, "Test")
.withField(StandardField.URL, "www.google.com")
.withField(BiblatexSoftwareField.REPOSITORY, "www.github.com")
- .withField(StandardField.DOI, "10.0000/COMPARE_OLDER_BACKUP")
+ .withField(StandardField.DOI, "10.0000/TEST")
.withField(StandardField.DATE, "2000-07-02")
.withField(StandardField.COMMENT, "Test entry.")
.withField(StandardField.ABSTRACT, "Test abstract.")
diff --git a/src/test/java/org/jabref/logic/util/io/CitationKeyBasedFileFinderTest.java b/src/test/java/org/jabref/logic/util/io/CitationKeyBasedFileFinderTest.java
index 7133acaa60e..3f29211f4e0 100644
--- a/src/test/java/org/jabref/logic/util/io/CitationKeyBasedFileFinderTest.java
+++ b/src/test/java/org/jabref/logic/util/io/CitationKeyBasedFileFinderTest.java
@@ -47,8 +47,8 @@ void setUp(@TempDir Path temporaryFolder) throws IOException {
Files.createFile(dir2003.resolve("Paper by HipKro03.pdf"));
Path dirTest = Files.createDirectory(rootDir.resolve("test"));
- Files.createFile(dirTest.resolve(".COMPARE_OLDER_BACKUP"));
- Files.createFile(dirTest.resolve("COMPARE_OLDER_BACKUP["));
+ Files.createFile(dirTest.resolve(".TEST"));
+ Files.createFile(dirTest.resolve("TEST["));
Files.createFile(dirTest.resolve("TE.ST"));
Files.createFile(dirTest.resolve("foo.dat"));
From dbe6f2b89a0fbafbc4f7f58ee8c232837b141ba6 Mon Sep 17 00:00:00 2001
From: Gillan0
Date: Fri, 6 Dec 2024 19:21:36 +0100
Subject: [PATCH 78/84] Added new UI text to JabRef_en.properties
---
src/main/resources/l10n/JabRef_en.properties | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties
index 32bc777fcc6..f552dad398b 100644
--- a/src/main/resources/l10n/JabRef_en.properties
+++ b/src/main/resources/l10n/JabRef_en.properties
@@ -2689,9 +2689,17 @@ Keep\ existing\ entry=Keep existing entry
No\ entries\ corresponding\ to\ given\ query=No entries corresponding to given query
Review\ backup=Review\ backup
-A\ backup\ file\ for\ '%0'\ was\ found\ at\ [%1]=A backup file for '%0' was found at [%1]
+A\ backup\ for\ '%0'\ was\ found.=A backup for '%0' was found.
Do\ you\ want\ to\ recover\ the\ library\ from\ the\ backup\ file?=Do you want to recover the library from the backup file?
This\ could\ indicate\ that\ JabRef\ did\ not\ shut\ down\ cleanly\ last\ time\ the\ file\ was\ used.=This could indicate that JabRef did not shut down cleanly last time the file was used.
+Choose\ backup\ file=Choose backup file
+Date\ of\ Backup=Date of Backup
+Do\ you\ want\ to\ recover\ the\ library\ from\ a\ backup\ file?=Do you want to recover the library from a backup file?
+It\ looks\ like\ JabRef\ did\ not\ shut\ down\ cleanly\ last\ time\ the\ file\ was\ used.=It looks like JabRef did not shut down cleanly last time the file was used.
+Number\ of\ Entries=Number of Entries
+Restore\ from\ latest\ backup=Restore from latest backup
+Review\ latest\ backup=Review latest backup
+Size\ of\ Backup=Size of Backup
Use\ the\ field\ FJournal\ to\ store\ the\ full\ journal\ name\ for\ (un)abbreviations\ in\ the\ entry=Use the field FJournal to store the full journal name for (un)abbreviations in the entry
From e0653994d0853c70b52677001b2d4b9a0f87985e Mon Sep 17 00:00:00 2001
From: Nawal CHAHBOUNE
Date: Sat, 7 Dec 2024 21:14:59 +0100
Subject: [PATCH 79/84] Resolve comment on BackupManagerGitTest.java and
---
buildres/abbrv.jabref.org | 2 +-
src/main/java/org/jabref/gui/LibraryTab.java | 2 +-
.../gui/autosaveandbackup/BackupManagerGit.java | 16 ++++++++++------
src/main/resources/csl-locales | 2 +-
src/main/resources/csl-styles | 2 +-
.../autosaveandbackup/BackupManagerGitTest.java | 14 +-------------
6 files changed, 15 insertions(+), 23 deletions(-)
diff --git a/buildres/abbrv.jabref.org b/buildres/abbrv.jabref.org
index 0fdf99147a8..d87037495de 160000
--- a/buildres/abbrv.jabref.org
+++ b/buildres/abbrv.jabref.org
@@ -1 +1 @@
-Subproject commit 0fdf99147a8a5fc8ae7ccd79ad4e0029e736e4a3
+Subproject commit d87037495de7213b896dbb6a20170387de170709
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index 99a2986be6c..6ab7334c880 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -374,7 +374,7 @@ public void installAutosaveManagerAndBackupManager() throws GitAPIException, IOE
autosaveManager.registerListener(new AutosaveUiManager(this, dialogService, preferences, entryTypesManager));
}
if (isDatabaseReadyForBackup(bibDatabaseContext) && preferences.getFilePreferences().shouldCreateBackup()) {
- BackupManagerGit.start(this, bibDatabaseContext, Injector.instantiateModelOrService(BibEntryTypesManager.class), preferences);
+ BackupManagerGit.start(this, bibDatabaseContext, entryTypesManager, preferences);
}
}
diff --git a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
index 115293072aa..d86a8425559 100644
--- a/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
+++ b/src/main/java/org/jabref/gui/autosaveandbackup/BackupManagerGit.java
@@ -9,11 +9,14 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
+import java.text.MessageFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -148,7 +151,10 @@ protected static String getOrGenerateFileUuid(Path filePath) throws IOException
public static void rewriteFile(Path dbFile, String content) throws IOException {
// Ensure the file exists before rewriting
if (!Files.exists(dbFile)) {
- throw new FileNotFoundException("The file at path " + dbFile + " does not exist.");
+ Locale currentLocale = Locale.getDefault();
+ ResourceBundle messages = ResourceBundle.getBundle("messages", currentLocale);
+ String errorMessage = MessageFormat.format(messages.getString("file.not.found"), dbFile.toString());
+ throw new FileNotFoundException(errorMessage);
}
// Write the new content to the file (overwrite mode)
@@ -266,7 +272,7 @@ void startBackupTask(Path backupDir, BibDatabaseContext bibDatabaseContext) {
() -> {
try {
Path dbFile = bibDatabaseContext.getDatabasePath().orElseThrow(() -> new IllegalArgumentException("Database path is not provided."));
- copyDatabaseFileToBackupDir(dbFile, backupDir);
+ // copyDatabaseFileToBackupDir(dbFile, backupDir);
performBackup(dbFile, backupDir);
} catch (IOException | GitAPIException e) {
LOGGER.error("Error during backup", e);
@@ -633,15 +639,13 @@ public static void performBackupNoCommits(Path dbFile, Path backupDir) throws IO
LOGGER.info("No commits found in the repository. We need a first commit.");
// Ensure the specific database file is copied to the backup directory
- copyDatabaseFileToBackupDir(dbFile, backupDir);
+ // no need of copying again !!
+ // copyDatabaseFileToBackupDir(dbFile, backupDir);
// Ensure the Git repository exists
LOGGER.info("Ensuring the .git is initialized");
ensureGitInitialized(backupDir);
- // Open the Git repository located in the backup directory
- Repository repository = openGitRepository(backupDir);
-
// Get the file name of the database file
String baseName = dbFile.getFileName().toString();
String uuid = getOrGenerateFileUuid(dbFile); // Generate or retrieve the UUID for this file
diff --git a/src/main/resources/csl-locales b/src/main/resources/csl-locales
index 4753e3a9aca..8bc2af16f51 160000
--- a/src/main/resources/csl-locales
+++ b/src/main/resources/csl-locales
@@ -1 +1 @@
-Subproject commit 4753e3a9aca4b806ac0e3036ed727d47bf8f678e
+Subproject commit 8bc2af16f5180a8e4fb591c2be916650f75bb8f6
diff --git a/src/main/resources/csl-styles b/src/main/resources/csl-styles
index 49af15c4f5b..b413a778b81 160000
--- a/src/main/resources/csl-styles
+++ b/src/main/resources/csl-styles
@@ -1 +1 @@
-Subproject commit 49af15c4f5bca025b6b18ca48c447016586f01e7
+Subproject commit b413a778b8170cf5aebbb9aeffec62cfd068e19e
diff --git a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
index dbd9022dd67..d6d20e47c20 100644
--- a/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
+++ b/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerGitTest.java
@@ -16,7 +16,6 @@
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -43,6 +42,7 @@ public class BackupManagerGitTest {
@BeforeEach
public void setUp(@TempDir Path tempDir) throws IOException, GitAPIException {
mockLibraryTab = mock(LibraryTab.class);
+
mockDatabaseContext1 = mock(BibDatabaseContext.class);
mockDatabaseContext2 = mock(BibDatabaseContext.class);
mockEntryTypesManager = mock(BibEntryTypesManager.class);
@@ -87,18 +87,6 @@ public void setUp(@TempDir Path tempDir) throws IOException, GitAPIException {
when(mockDatabaseContext2.getMetaData()).thenReturn(mockMetaData2);
}
- @AfterEach
- void tearDown() throws IOException {
- // Delete the temporary directory
- Files.walk(tempDir)
- .map(Path::toFile)
- .forEach(file -> {
- if (!file.delete()) {
- file.deleteOnExit();
- }
- });
- }
-
@Test
void initializationCreatesBackupDirectory() throws IOException, GitAPIException {
// Create BackupManagerGit
From da79d5d71511ce4f4d55a813a0991dbf8c93f59a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9CKhaoula?=
<“khaoula.arouissi@imt-atlantique.net”>
Date: Fri, 13 Dec 2024 22:48:46 +0100
Subject: [PATCH 80/84] Resolve comment about BackupUIManager.java
---
src/main/java/org/jabref/gui/dialogs/BackupUIManager.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index 58a124e611b..21760488bb8 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -110,8 +110,9 @@ private static Optional showBackupResolverDialog(DialogService dialo
private static Optional showBackupChoiceDialog(DialogService dialogService,
GuiPreferences preferences,
List backups) {
+ Path backupDirectory = preferences.getFilePreferences().getBackupDirectory();
return UiTaskExecutor.runInJavaFXThread(
- () -> dialogService.showCustomDialogAndWait(new BackupChoiceDialog(preferences.getFilePreferences().getBackupDirectory(), backups)));
+ () -> dialogService.showCustomDialogAndWait(new BackupChoiceDialog(backupDirectory, backups)));
}
private static Optional