Fix newer notes incorrectly being skipped when importing successive exports (#3693)

* add Note::set_modified_with_mtime

* add struct for Collection::update_note_inner_without_cards's args

* refactor Collection::update_note_inner_without_cards to use the arg struct

* add Collection::update_note_inner_without_cards_using_mtime

* use incoming note's mtime when updating notes during import
This commit is contained in:
llama 2025-01-13 10:42:44 +08:00 committed by GitHub
parent afd7fca4cb
commit 2a1448bc45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 86 additions and 48 deletions

View file

@ -15,6 +15,7 @@ use crate::import_export::package::UpdateCondition;
use crate::import_export::ImportError; use crate::import_export::ImportError;
use crate::import_export::ImportProgress; use crate::import_export::ImportProgress;
use crate::import_export::NoteLog; use crate::import_export::NoteLog;
use crate::notes::UpdateNoteInnerWithoutCardsArgs;
use crate::notetype::ChangeNotetypeInput; use crate::notetype::ChangeNotetypeInput;
use crate::prelude::*; use crate::prelude::*;
use crate::progress::ThrottlingProgressHandler; use crate::progress::ThrottlingProgressHandler;
@ -463,14 +464,20 @@ impl<'n> NoteContext<'n> {
self.munge_media(&mut note)?; self.munge_media(&mut note)?;
let original = self.get_expected_note(note.id)?; let original = self.get_expected_note(note.id)?;
let notetype = self.get_expected_notetype(note.notetype_id)?; let notetype = self.get_expected_notetype(note.notetype_id)?;
self.target_col.update_note_inner_without_cards( // Preserve the incoming note's mtime to allow imports of successive exports
&mut note, let incoming_mtime = note.mtime;
&original, self.target_col
&notetype, .update_note_inner_without_cards_using_mtime(
self.usn, UpdateNoteInnerWithoutCardsArgs {
true, note: &mut note,
self.normalize_notes, original: &original,
true, notetype: &notetype,
usn: self.usn,
mark_note_modified: true,
normalize_text: self.normalize_notes,
update_tags: true,
},
Some(incoming_mtime),
)?; )?;
self.imports.log_updated(note, source_id); self.imports.log_updated(note, source_id);
Ok(()) Ok(())

View file

@ -220,11 +220,16 @@ impl Note {
Ok(()) Ok(())
} }
pub(crate) fn set_modified(&mut self, usn: Usn) { #[inline]
self.mtime = TimestampSecs::now(); pub(crate) fn set_modified_with_mtime(&mut self, usn: Usn, mtime: TimestampSecs) {
self.mtime = mtime;
self.usn = usn; self.usn = usn;
} }
pub(crate) fn set_modified(&mut self, usn: Usn) {
self.set_modified_with_mtime(usn, TimestampSecs::now())
}
pub(crate) fn nonempty_fields<'a>(&self, fields: &'a [NoteField]) -> HashSet<&'a str> { pub(crate) fn nonempty_fields<'a>(&self, fields: &'a [NoteField]) -> HashSet<&'a str> {
self.fields self.fields
.iter() .iter()
@ -346,6 +351,18 @@ fn invalid_char_for_field(c: char) -> bool {
c.is_ascii_control() && c != '\n' && c != '\t' c.is_ascii_control() && c != '\n' && c != '\t'
} }
/// Used when calling [Collection::update_note_inner_without_cards] and
/// [Collection::update_note_inner_without_cards_using_mtime]
pub(crate) struct UpdateNoteInnerWithoutCardsArgs<'a> {
pub(crate) note: &'a mut Note,
pub(crate) original: &'a Note,
pub(crate) notetype: &'a Notetype,
pub(crate) usn: Usn,
pub(crate) mark_note_modified: bool,
pub(crate) normalize_text: bool,
pub(crate) update_tags: bool,
}
impl Collection { impl Collection {
pub(crate) fn canonify_note_tags(&mut self, note: &mut Note, usn: Usn) -> Result<()> { pub(crate) fn canonify_note_tags(&mut self, note: &mut Note, usn: Usn) -> Result<()> {
if !note.tags.is_empty() { if !note.tags.is_empty() {
@ -434,40 +451,53 @@ impl Collection {
normalize_text: bool, normalize_text: bool,
update_tags: bool, update_tags: bool,
) -> Result<()> { ) -> Result<()> {
self.update_note_inner_without_cards( self.update_note_inner_without_cards(UpdateNoteInnerWithoutCardsArgs {
note, note,
original, original,
ctx.notetype, notetype: ctx.notetype,
ctx.usn, usn: ctx.usn,
mark_note_modified, mark_note_modified,
normalize_text, normalize_text,
update_tags, update_tags,
)?; })?;
self.generate_cards_for_existing_note(ctx, note) self.generate_cards_for_existing_note(ctx, note)
} }
// TODO: refactor into struct #[inline]
#[allow(clippy::too_many_arguments)] pub(crate) fn update_note_inner_without_cards_using_mtime(
pub(crate) fn update_note_inner_without_cards(
&mut self, &mut self,
note: &mut Note, UpdateNoteInnerWithoutCardsArgs {
original: &Note, note,
notetype: &Notetype, original,
usn: Usn, notetype,
mark_note_modified: bool, usn,
normalize_text: bool, mark_note_modified,
update_tags: bool, normalize_text,
update_tags,
}: UpdateNoteInnerWithoutCardsArgs,
mtime: Option<TimestampSecs>,
) -> Result<()> { ) -> Result<()> {
if update_tags { if update_tags {
self.canonify_note_tags(note, usn)?; self.canonify_note_tags(note, usn)?;
} }
note.prepare_for_update(notetype, normalize_text)?; note.prepare_for_update(notetype, normalize_text)?;
if mark_note_modified { if mark_note_modified {
if let Some(mtime) = mtime {
note.set_modified_with_mtime(usn, mtime);
} else {
note.set_modified(usn); note.set_modified(usn);
} }
}
self.update_note_undoable(note, original) self.update_note_undoable(note, original)
} }
pub(crate) fn update_note_inner_without_cards(
&mut self,
args: UpdateNoteInnerWithoutCardsArgs<'_>,
) -> Result<()> {
self.update_note_inner_without_cards_using_mtime(args, None)
}
pub(crate) fn remove_notes_inner(&mut self, nids: &[NoteId], usn: Usn) -> Result<usize> { pub(crate) fn remove_notes_inner(&mut self, nids: &[NoteId], usn: Usn) -> Result<usize> {
let mut card_count = 0; let mut card_count = 0;
for nid in nids { for nid in nids {
@ -542,15 +572,15 @@ impl Collection {
out.update_tags, out.update_tags,
)?; )?;
} else { } else {
self.update_note_inner_without_cards( self.update_note_inner_without_cards(UpdateNoteInnerWithoutCardsArgs {
&mut note, note: &mut note,
&original, original: &original,
&nt, notetype: &nt,
usn, usn,
out.mark_modified, mark_note_modified: out.mark_modified,
norm, normalize_text: norm,
out.update_tags, update_tags: out.update_tags,
)?; })?;
} }
changed_notes += 1; changed_notes += 1;

View file

@ -9,6 +9,7 @@ use std::mem;
use super::CardGenContext; use super::CardGenContext;
use super::CardTemplate; use super::CardTemplate;
use super::Notetype; use super::Notetype;
use crate::notes::UpdateNoteInnerWithoutCardsArgs;
use crate::prelude::*; use crate::prelude::*;
use crate::search::JoinSearches; use crate::search::JoinSearches;
use crate::search::TemplateKind; use crate::search::TemplateKind;
@ -80,15 +81,15 @@ impl Collection {
for nid in nids { for nid in nids {
let mut note = self.storage.get_note(nid)?.unwrap(); let mut note = self.storage.get_note(nid)?.unwrap();
let original = note.clone(); let original = note.clone();
self.update_note_inner_without_cards( self.update_note_inner_without_cards(UpdateNoteInnerWithoutCardsArgs {
&mut note, note: &mut note,
&original, original: &original,
nt, notetype: nt,
usn, usn,
true, mark_note_modified: true,
normalize_text, normalize_text,
false, update_tags: false,
)?; })?;
} }
} else { } else {
// nothing to do // nothing to do
@ -104,15 +105,15 @@ impl Collection {
let mut note = self.storage.get_note(nid)?.unwrap(); let mut note = self.storage.get_note(nid)?.unwrap();
let original = note.clone(); let original = note.clone();
note.reorder_fields(&ords); note.reorder_fields(&ords);
self.update_note_inner_without_cards( self.update_note_inner_without_cards(UpdateNoteInnerWithoutCardsArgs {
&mut note, note: &mut note,
&original, original: &original,
nt, notetype: nt,
usn, usn,
true, mark_note_modified: true,
normalize_text, normalize_text,
false, update_tags: false,
)?; })?;
} }
Ok(()) Ok(())
} }