Correctly handle paths when moving hunks

finchie
Dec 17, 2025, 12:26 PM
J4I5CGHR2653TYDOHXFJPUBGWHVDBKP74IE7JEPAJ44IY5JFNQNQC

Dependencies

  • [2] WFWTKCJN Create initial Visual Studio Code extension
  • [3] TIPGJCUW Consistently use `EditorWorkingCopy` in `pijul-extension`
  • [4] DUZBRB3U Render correspoding hunk on inline credit hover
  • [5] LQJG2LGQ Refactor `PathState` handling to be updated incrementally
  • [6] TRJASCAZ Use a trie to keep track of path states
  • [7] 37JT3GCX Lazily load channel state instead of storing in memory

Change contents

  • replacement in pijul-extension/src/path_state.rs at line 8
    [5.208][2.6961:7001](),[2.6961][2.6961:7001]()
    use libpijul::changestore::ChangeStore;
    [5.208]
    [2.7001]
    use libpijul::changestore::{ChangeStore, FileMetadata};
  • edit in pijul-extension/src/path_state.rs at line 10
    [2.7062]
    [2.7062]
    use libpijul::pristine::{ChangePosition, Position};
  • edit in pijul-extension/src/path_state.rs at line 34
    [2.7408]
    [5.873]
    FindPath(#[from] libpijul::output::FileError<C, MutTxn<()>>),
  • replacement in pijul-extension/src/path_state.rs at line 47
    [5.1024][5.1024:1046]()
    pub fn join_hunk(
    [5.1024]
    [5.1046]
    pub fn join_hunk<Hash: std::fmt::Debug, Local: std::fmt::Debug>(
  • replacement in pijul-extension/src/path_state.rs at line 49
    [5.1075][5.1075:1145]()
    hunk: &Hunk<Option<libpijul::Hash>, libpijul::change::Local>,
    [5.1075]
    [5.1145]
    hunk: &Hunk<Option<Hash>, Local>,
  • replacement in pijul-extension/src/path_state.rs at line 67
    [5.1989][5.1989:2072]()
    Some(TrackedState::Moved) => Some(TrackedState::ModifiedAndMoved),
    [5.1989]
    [5.2072]
    Some(TrackedState::Moved | TrackedState::ModifiedAndMoved) => {
    Some(TrackedState::ModifiedAndMoved)
    }
  • edit in pijul-extension/src/path_state.rs at line 190
    [5.5910]
    [6.244]
    let change_contents = unrecorded_state.contents.lock();
    let new_inodes: HashMap<ChangePosition, Utf8PathBuf> = unrecorded_state
    .actions
    .iter()
    .filter_map(|hunk| {
    if let Hunk::FileAdd {
    add_inode, path, ..
    } = hunk
    {
    // `add_inode` should always be Atom::NewVertex
    Some((add_inode.as_newvertex().start, Utf8PathBuf::from(path)))
    } else {
    None
    }
    })
    .collect();
  • replacement in pijul-extension/src/path_state.rs at line 211
    [5.5958][5.5958:6031]()
    let globalized_hunk = hunk.globalize(&*transaction.read())?;
    [5.5958]
    [5.6031]
    let path = if let Hunk::FileMove { add, .. } = &hunk {
    let add_vertex = add.as_newvertex();
    let file_metadata = FileMetadata::read(
    &change_contents[add_vertex.start.0.as_usize()..add_vertex.end.0.as_usize()],
    );
    let parent_inode = add_vertex.inode;
    let current_parent_path = if let Some(change) = parent_inode.change {
    // Parent is already tracked
    let (path, _is_alive) = libpijul::fs::find_path(
    change_store,
    &*transaction.read(),
    &*channel.read(),
    true,
    Position {
    change,
    pos: parent_inode.pos,
    },
    )?
    .unwrap();
    Utf8PathBuf::from(path)
    } else {
    // Parent is not yet tracked
    new_inodes.get(&parent_inode.pos).unwrap().to_path_buf()
    };
    current_parent_path.join(file_metadata.basename).to_string()
    } else {
    let globalized_hunk = hunk.clone().globalize(&*transaction.read())?;
    globalized_hunk.path().to_string()
    };
  • replacement in pijul-extension/src/path_state.rs at line 245
    [5.6032][6.295:377]()
    let entry = updated_states.entry(globalized_hunk.path().to_string());
    [5.6032]
    [5.6118]
    let entry = updated_states.entry(path);
  • replacement in pijul-extension/src/path_state.rs at line 254
    [5.6472][5.6472:6609]()
    if let Some(updated_state) =
    TrackedState::join_hunk(existing_tracked_state, &globalized_hunk)
    {
    [5.6472]
    [5.6609]
    if let Some(updated_state) = TrackedState::join_hunk(existing_tracked_state, &hunk) {
  • replacement in pijul-extension/src/path_state.rs at line 257
    [5.6701][5.6701:6789]()
    tracing::info!(message = "Skipping unrecorded hunk", ?globalized_hunk);
    [5.6701]
    [2.9670]
    tracing::info!(message = "Skipping unrecorded hunk", ?hunk);
  • edit in pijul-extension/src/path_state.rs at line 261
    [5.6791]
    [6.378]
    tracing::debug!(?updated_states);
  • edit in pijul-extension/src/path_state.rs at line 293
    [6.1641]
    [6.1641]
    tracing::debug!(?self.states);
  • replacement in pijul-extension/src/lib.rs at line 23
    [2.11192][5.7786:7857]()
    use libpijul::{ArcTxn, ChangeId, ChannelRef, GraphTxnT, TxnT, Vertex};
    [2.11192]
    [3.0]
    use libpijul::{ArcTxn, ChangeId, ChannelRef, GraphTxnT, MutTxnTExt, TxnT, Vertex};
  • edit in pijul-extension/src/lib.rs at line 101
    [4.286]
    [4.286]
    pub enum MovePathError<C: std::error::Error + 'static, W: std::error::Error + 'static> {
    #[error("Unable to begin transaction: {0}")]
    BeginTransaction(#[from] BeginTransactionError),
    #[error(transparent)]
    Sanakirja(#[from] SanakirjaError),
    #[error("Failed to move paths: {0}")]
    Move(#[from] libpijul::fs::FsError<MutTxn<()>>),
    #[error(transparent)]
    PathStates(#[from] PathStatesError<C, W>),
    }
    #[derive(Debug, thiserror::Error)]
  • edit in pijul-extension/src/lib.rs at line 400
    [5.9127]
    [5.9127]
    &transaction,
    &channel,
    &self.working_copy.working_copy,
    &self.change_store,
    )?;
    Ok(())
    }
    pub fn move_path(
    &mut self,
    from_path: &Utf8Path,
    to_path: Utf8PathBuf,
    ) -> Result<(), MovePathError<C::Error, W::Error>> {
    let move_transaction = self.pristine.arc_txn_begin()?;
    move_transaction
    .write()
    .move_file(from_path.as_str(), to_path.as_str(), 0)?;
    move_transaction.commit()?;
    let (transaction, channel) = begin_transaction(&self.pristine)?;
    self.path_states.update_path_state(
    to_path,