Skip to content

Use .bib extension for backup files - #16675

Open
koppor wants to merge 4 commits into
mainfrom
backup-bak-to-bib
Open

Use .bib extension for backup files#16675
koppor wants to merge 4 commits into
mainfrom
backup-bak-to-bib

Conversation

@koppor

@koppor koppor commented Aug 25, 2026

Copy link
Copy Markdown
Member

Summary

🤖 JabRef's automatic backups in the user data directory carried the .bak extension, so a user who locates them cannot open them in JabRef directly (see this comment). Backups are now written with the .bib extension; backups written earlier with .bak are still found and rotated as before. First of several PRs for #11454 — the issue stays open.

jabref-contrib-policy:4.2:reviewed​:ok

Analogies: Like honey, this change is small and keeps: nothing is lost, only the label changes. Like chocolate, it should go down easily: one constant. Like the moon, the backups are the same as before, just showing a more familiar face.

Steps to test

  1. Open a library, change an entry, wait about 20 seconds.
  2. Open the backup directory (Preferences → Linked files → Backup directory; default: <user data dir>/org.jabref/jabref/backups).
  3. The newest file is named <prefix>--<library>.bib--<date>--<time>.bib and opens via File → Open library.
  4. Existing .bak backups in that directory are still offered by the "Backup found" dialog when they are newer than the library.

"Backup found" dialog naming the .bib backup (top) and the backup file opened as a library (bottom):

Backup found dialog and the backup opened in JabRef

Related issues and pull requests

Refs #11454 (not closed — first step of the checklist in #11454 (comment))

User documentation: JabRef/user-documentation#661

AI usage

Claude Code (model claude-fable-5), AIL3 — AI-generated under human direction, reviewed by the contributor.

AI CHECKLIST.md walkthrough

1. Code self-review

Nullability and control flow

Not applicable: no control flow changed (one constant plus comments).

  • [/] No == null / != null checks — JSpecify annotations (@NullMarked, @Nullable, @NonNull) used instead.
  • [/] No Objects.requireNonNull(...) — nullability expressed via JSpecify annotations.
  • [/] New classes annotated with @NullMarked (org.jspecify.annotations.NullMarked).
  • [/] Optional consumed with ifPresent / ifPresentOrElse / map / orElseThrow — never orElse(unusedValue) nor an isPresent() + get() block.
  • [/] StringUtil.isBlank(...) used instead of s == null || s.isBlank().

Exceptions

Not applicable: no exception handling touched.

  • [/] No catch (Exception e) — only specific exceptions are caught.
  • [/] No throw new RuntimeException(...) / IllegalStateException(...) — these tear down the whole application.
  • [/] Logged exceptions are passed as the last logger argument (LOGGER.info("...", e)), not concatenated into the message string.

Style and idioms

  • [/] New BibEntry objects built with withers (withField, not setField).
  • [/] Modern Java used: List.of() / Map.of() / Set.of(), Path.of(), SequencedCollection / SequencedSet, text blocks.
  • [/] Regexes use a precompiled Pattern.compile(...) constant, not String.matches(...).
  • [/] Background work uses org.jabref.logic.util.BackgroundTask, not new Thread().
  • No commented-out code, no trivial comments restating the code, no AI-disclosure comments in source.
  • Markdown Javadoc (///) uses Markdown syntax, not JavaDoc inline tags: `code` instead of {@code}, [ClassName] instead of {@link}.

User-facing text

Not applicable: no user-facing text.

  • [/] All user-facing text localized (Localization.lang in Java, % prefix in FXML).
  • [/] Sentence case (not Title Case); no trailing !; labels do not end with :.
  • [/] Variance expressed with placeholders ("...: %0"), not string concatenation.

Security

  • [/] User-controlled data (request params, entry fields, file contents) is HTML-escaped before being written into any text/html response — including exception/error messages, not just the success body (XSS).

Tests

  • Behavior changes in org.jabref.model / org.jabref.logic have added or updated tests.
  • Tests assert object contents (assertEquals), use plain JUnit asserts (not AssertJ), have no @DisplayName, do not catch exceptions (let them propagate so JUnit reports setup/teardown failures directly), and use @TempDir instead of manual temp directories.
  • [/] Fetcher tests hit the live endpoints — the remote API is not mocked or stubbed (automated-review suggestions to mock it are rejected on purpose).

2. Verification commands

  • ./gradlew :jablib:check (or ./gradlew check for all modules).
  • ./gradlew checkstyleMain checkstyleTest checkstyleJmh.
  • ./gradlew modernizer.
  • ./gradlew --no-configuration-cache :rewriteDryRun reports no changes (run ./gradlew rewriteRun to fix).
  • [/] ./gradlew javadoc.
  • npx markdownlint-cli2 "docs/**/*.md" "*.md" (only if Markdown changed).
  • [/] Only if formatting is still off after rewriteRun: docker run -v $(pwd):/github/workspace ghcr.io/leventebajczi/intellij-format:master "*.java" "" ".idea/codeStyles/Project.xml".

3. Documentation

  • CHANGELOG.md entry added if the change is visible to the user (end-user wording, no extra blank lines). Link the issue if one exists; link the PR only when no issue exists. Use TODO as the placeholder when neither is known yet — never a fake number.
  • Searched jabref/issues and jabref-koppor/issues for a related issue; linked only on a confident match, otherwise kept TODO (no closes/fixes for merely-similar issues).
  • [/] Requirement added to docs/requirements/<area>.md if the change is a new feature or significant bug fix (skip for refactors, minor fixes, and internal changes).
  • [/] Developer documentation under docs/ updated if behavior or architecture changed.

4. Pull request

  • PR body built from .github/PULL_REQUEST_TEMPLATE.md, every section filled.
  • All checklist items kept and marked [x], [ ], or [/].
  • All HTML comments removed from the PR body.
  • PR created with gh pr create --body-file <file> (not --body).
  • [/] If CHANGELOG.md used a TODO placeholder (no issue confidently identified yet — an existing issue link always stays), it was replaced with the real PR-number link after PR creation, then committed and pushed. If an issue is identified or created later, the link is switched to the issue.

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • If AI tools were used, I disclosed them in the "AI usage" section and reviewed, understood, and take full ownership of all AI-generated code
  • I manually tested my changes in running JabRef (always required)
  • I added JUnit tests for changes (if applicable)
  • [/] I added screenshots in the PR description (if change is visible to the user)
  • I added one sentence (max 20 words) to CHANGELOG.md describing the change from the user's point of view (if the change is visible to the user)
  • I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository

🤖 Generated with Claude Code

Backups in the user data directory were named .bak, so a user who
locates them could not open them in JabRef directly. Backups written
earlier with .bak are still found, because lookups match on the file
name prefix only.

Refs #11454

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012z49ZUMghSxLgLgJtQpo4g
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Write automatic backups with the .bib extension

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 10-20 Minutes

Grey Divider

AI Description

• Writes automatic backups with .bib extensions so JabRef can open them directly.
• Preserves discovery and rotation of legacy .bak backups through prefix-based lookup.
• Updates fallback expectations, compatibility coverage, comments, and changelog.
Diagram

graph TD
  A["Library change"] --> B["Backup manager"] --> C["Backup utility"] --> D[("Backup directory")]
  E["Backup type"] -->|.bib extension| C
  D -->|new or legacy| F["Backup recovery"]
Loading
High-Level Assessment

Changing the existing BackupFileType.BACKUP extension is the most direct approach because all backup path generation already consumes that enum. Retaining prefix-based discovery avoids migration work and preserves compatibility with legacy .bak files.

Files changed (8) +15 / -11

Bug fix (1) +4 / -2
BackupFileType.javaGenerate automatic backups as .bib files +4/-2

Generate automatic backups as .bib files

• Changes the automatic backup extension from 'bak' to 'bib', allowing generated backups to open directly in JabRef. Documents why legacy '.bak' files remain discoverable.

jablib/src/main/java/org/jabref/logic/util/BackupFileType.java

Tests (2) +4 / -3
BackupManagerTest.javaDocument legacy .bak compatibility coverage +2/-1

Document legacy .bak compatibility coverage

• Clarifies that the older backup fixtures intentionally retain '.bak', verifying that prefix-based selection still considers backups created before the extension change.

jabgui/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java

BackupFileUtilTest.javaExpect .bib extension for fallback backups +2/-2

Expect .bib extension for fallback backups

• Updates the directory-creation failure assertion to expect 'test.bib.bib', confirming that fallback backup paths use the new extension.

jablib/src/test/java/org/jabref/logic/util/io/BackupFileUtilTest.java

Documentation (5) +7 / -6
CHANGELOG.mdDocument openable .bib backup files +1/-0

Document openable .bib backup files

• Adds a user-facing changelog entry explaining that automatic backups now use '.bib' and can be opened directly in JabRef.

CHANGELOG.md

BackupManager.javaClarify separation from save-time .sav files +1/-1

Clarify separation from save-time .sav files

• Corrects the class documentation to distinguish automatic backups managed here from '.sav' files created during saving.

jabgui/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java

SaveConfiguration.javaCorrect save-backup extension documentation +1/-1

Correct save-backup extension documentation

• Updates the 'withMakeBackup' parameter documentation to identify save-time backups as '.sav' files rather than automatic '.bak' files.

jablib/src/main/java/org/jabref/logic/exporter/SaveConfiguration.java

JabRefCliPreferences.javaMake backup-directory rationale extension-neutral +1/-1

Make backup-directory rationale extension-neutral

• Generalizes the preference comment so the data-directory persistence rationale no longer assumes a '.bak' extension.

jablib/src/main/java/org/jabref/logic/preferences/JabRefCliPreferences.java

BackupFileUtil.javaRemove obsolete .bak assumptions from path documentation +3/-3

Remove obsolete .bak assumptions from path documentation

• Rewords fallback, storage, and suffix documentation to remain accurate after automatic backups switch to '.bib'. Path generation and lookup logic are unchanged.

jablib/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. BACKUP drops fallback compatibility ✓ Resolved 📎 Requirement gap ≡ Correctness
Description
Changing BackupFileType.BACKUP from bak to bib makes the missing-backup-directory recovery
branch construct only the current sidecar name (for example, .bib.bib) and stop checking the
corresponding legacy .bak sidecar (for example, .bib.bak). As a result, recovery comparison,
review, and restoration can report no backup even when recoverable legacy bibliography data exists
beside the library after backup-directory creation failed.
Code

jablib/src/main/java/org/jabref/logic/util/BackupFileType.java[13]

+    BACKUP("Backup", "bib"),
Evidence
Rule 3 requires backward compatibility, while rule 2 requires recovery to use available bibliography
backup data. Backup creation falls back to appending the configured extension beside the target when
directory creation fails, but the changed enum now exposes only bib; although normal
backup-directory discovery is prefix-based, BackupFileUtil constructs only one path from that
current extension when backupDir is absent and never tries the legacy .bak path. BackupManager
and BackupResolverDialog rely on this lookup for difference detection, crash-recovery review, and
restoration, so the legacy backup is missed throughout the recovery flow.

Restore the most recent library without data loss
AGENTS.md: Preserve Existing Architecture, Compatibility, and Focused Scope: AGENTS.md: Preserve Existing Architecture, Compatibility, and Focused Scope: AGENTS.md: Preserve Existing Architecture, Compatibility, and Focused Scope: AGENTS.md: Preserve Existing Architecture, Compatibility, and Focused Scope: AGENTS.md: Preserve Existing Architecture, Compatibility, and Focused Scope: AGENTS.md: Preserve Existing Architecture, Compatibility, and Focused Scope: AGENTS.md: Preserve Existing Architecture, Compatibility, and Focused Scope: AGENTS.md: Preserve Existing Architecture, Compatibility, and Focused Scope
jablib/src/main/java/org/jabref/logic/util/BackupFileType.java[10-13]
jablib/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java[53-65]
jabgui/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java[29-39]
jablib/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java[35-46]
jabgui/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java[131-175]
jabgui/src/main/java/org/jabref/gui/autosaveandbackup/BackupManager.java[178-189]
jabgui/src/test/java/org/jabref/gui/autosaveandbackup/BackupManagerTest.java[95-103]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Preserve discovery of legacy `.bak` sidecar backups after changing newly written backups to `.bib`.
## Issue Context
When the configured backup directory cannot be created, backup creation falls back to writing a sidecar beside the target. When that directory is absent during lookup, `getPathOfLatestExistingBackupFile` derives exactly one sidecar path from `BackupFileType.BACKUP`; because the PR changes that type's extension to `bib`, recovery checks the current `.bib` sidecar but no longer checks the corresponding legacy `.bak` sidecar. Normal backup-directory discovery is prefix-based and handles both extensions, but this missing-directory fallback does not.
Continue writing new backups as `.bib`, but search both the current `.bib` and legacy `.bak` sidecar names in the absent-directory branch, selecting and returning the newest applicable backup when both exist. Add deterministic test coverage for legacy fallback discovery.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java[53-65]
- jablib/src/test/java/org/jabref/logic/util/io/BackupFileUtilTest.java[44-57]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

koppor and others added 3 commits August 25, 2026 15:16
The fallback lookup next to the library derived a single sidecar name
from the backup extension, so .bak sidecars written by older versions
were no longer found. All extensions of the backup type are tried now,
and the newest existing sidecar wins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012z49ZUMghSxLgLgJtQpo4g
…tern

When the backup directory cannot be created, the backup was written as
library.bib.bak next to the library. It now uses the same
<prefix>--<library>--<timestamp>.bib name as in the backup directory,
so it opens in JabRef and rotates like the others. Lookups search the
same location; a .bak sidecar of older versions is still found when no
other backup exists.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012z49ZUMghSxLgLgJtQpo4g
@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Aug 27, 2026
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Your pull request conflicts with the target branch.

Please merge upstream/main with your code. For a step-by-step guide to resolve merge conflicts, see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants