filter to-record changes from selection
[?]
Nov 21, 2025, 8:08 AM
UPWS6J3BIHQKXSSWHD7CFLJOXWT3MRABFRVQ4T4NRYFALBAKJOOQCDependencies
- [2]
SWWE2R6Mdisplay basic repo stuff - [3]
EC3TVL4Xadd untracked files - [4]
KT5UYXGKfix selection after adding file, add changed file diffs - [5]
W7IUT3ZVstart recording impl - [6]
YBJRDOTCmake all repo actions async - [7]
A5YBC77Vrecord! - [8]
4WO3ZJM2show untracked files' contents - [9]
W4LFX7IHgroup diffs by file name - [10]
AMPZ2BXKshow changed files diffs (only Edit atm) - [11]
RPCIGCNSadd replacement diff details - [12]
OQ6HSAWHshow record log - [13]
WI2BVQ6Jrm client lib crate - [14]
CALXOZXAflatten crates dir - [15]
VCNKFNUFapp init test - [16]
KQ3P3QRLupdate comments - [17]
FL2ULDJNtest record - [18]
KWTBNTO3diffs selection and scrolling - [19]
WAOGSCOJvery nice refactor, wip adding channels logs - [20]
WH57EHNMupdate tests - [21]
AZ5D2LQUallow to set record description - [22]
IFQPVMBDerror handling for repo actions - [23]
LFEMJYYDstart of to_record selection - [24]
5O4FWCFPadd tests to_record selection and improve it - [25]
HPSOAD4Rfix moved tracked file view - [26]
2SLTGWP6add change files diffs to-record selection - [27]
UCBNZULEmake changed files paths optional (no path for root) - [28]
4ELJZGRJload and store all change diffs at once - [29]
ONRCENKTrm unnecessary state from repo's internal state - [30]
OJPGHVC3entire log! - [31]
SWDPAGF6test channel name - [32]
JE44NYHMdisplay log files diffs - [33]
I56UGW7Umake record test, fix log update - [34]
23SFYK4Qbig view refactor into a new crate - [*]
UF5NJKAStest load repo - [*]
6YZAVBWUInitial commit
Change contents
- file addition: to_record.rs[14.31]
use libpijul::HashMap;use crate::{diff, repo};#[derive(Clone, Copy, Debug, PartialEq, Eq)]pub enum Pick {Include,Exclude,}#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]pub enum PickSet {#[default]Include,Exclude,Partial,}#[derive(Clone, Debug, Default)]pub struct State {/// State of all filespub overall: PickSet,/// Map from file name to its changes state. This is preserved even when/// `overall` selection changes.pub files: HashMap<String, PickSet>,/// Map from file name to its individual change state. This is preserved/// even when `overall` and `files` selection changespub changes: HashMap<String, PartialFile>,}#[derive(Clone, Debug, Default)]pub struct PartialFile {/// Map from file name to its state// NOTE: When it's possible to select individual lines/chars to record,// then the value would be `PickSet` and the inner-most value of// selected ranges would be `Pick`pub changes: HashMap<diff::IdHash, Pick>,}/// Determine the default value for a file that is not explicitly set.pub fn default_pick_set(state: &State) -> PickSet {match default_pick(state) {Pick::Include => PickSet::Include,Pick::Exclude => PickSet::Exclude,}}/// Determine the default value for a change that is not explicitly set.pub fn default_pick(state: &State) -> Pick {let State {overall,files,changes: _,} = state;if matches!(overall, PickSet::Partial)|| files.values().any(|pick| matches!(pick, PickSet::Partial | PickSet::Exclude)){Pick::Exclude} else {Pick::Include}}pub fn determine_file(state: &State,file: &str,changed_files: &repo::ChangedFiles,) -> PickSet {match state.overall {PickSet::Include => PickSet::Include,PickSet::Exclude => PickSet::Exclude,PickSet::Partial => {if let Some(pick) = state.files.get(file) {*pick} else if file_has_any_partial_change(file, state, changed_files) {PickSet::Partial} else {default_pick_set(state)}}}}pub fn determine_change(file: &str,diff_id: diff::IdHash,state: &State,) -> Pick {match state.overall {PickSet::Include => return Pick::Include,PickSet::Exclude => return Pick::Exclude,PickSet::Partial => {}}if let Some(pick) = state.files.get(file) {match pick {PickSet::Include => return Pick::Include,PickSet::Exclude => return Pick::Exclude,PickSet::Partial => {}}}let explicit = state.changes.get(file).and_then(|file| file.changes.get(&diff_id)).copied();dbg!(&explicit);explicit.unwrap_or_else(|| default_pick(state))}pub fn file_has_any_partial_change(file: &str,state: &State,changed_files: &repo::ChangedFiles,) -> bool {let changes = changed_files.get(file).unwrap();let (mut has_include, mut has_exclude) = (false, false);state.changes.get(file).map(|file| {if file.changes.len() == changes.len() {// The number of of changes in selection is the same as number// of actual changes, we need to have both exclude and include// to be partialfile.changes.values().any(|pick| {has_exclude |= matches!(pick, Pick::Exclude);has_include |= matches!(pick, Pick::Include);has_include && has_exclude})} else {// There is the same number of changes in selection is not the// same as number of actual changes, we only need to find 1// exclusion to be partialfile.changes.values().any(|pick| matches!(pick, Pick::Exclude))}}).unwrap_or_default()} - replacement in libflorescence/src/testing.rs at line 85
repo::record(&internal, "Initialized".to_string(), None, skey).unwrap();let to_record = crate::to_record::State::default();repo::record(&internal, "Initialized".to_string(), None, skey, to_record).unwrap(); - edit in libflorescence/src/repo.rs at line 9
use crate::{diff, to_record}; - edit in libflorescence/src/repo.rs at line 65
to_record: to_record::State, - replacement in libflorescence/src/repo.rs at line 188
pub type Diff = LocalChange<Hunk<Option<ChangeHash>, Local>, Author>;pub type Diff = LocalChange<DiffHunk, Author>;pub type DiffHunk = Hunk<Option<ChangeHash>, Local>; - replacement in libflorescence/src/repo.rs at line 317
MsgIn::Record { msg, desc, sk } => {MsgIn::Record {msg,desc,sk,to_record,} => { - replacement in libflorescence/src/repo.rs at line 326
record(internal_state, msg, desc, sk)?;record(internal_state, msg, desc, sk, to_record)?; - edit in libflorescence/src/repo.rs at line 564
to_record: to_record::State, - edit in libflorescence/src/repo.rs at line 567
// These two variable are needed to for `to_record` change filter.let diff = diff(repo)?;let changed_files = changed_files(&diff, &repo.changes)?; - edit in libflorescence/src/repo.rs at line 652
// Filter changes using `to_record`match to_record.overall {to_record::PickSet::Exclude => {bail!("Nothing to record")}to_record::PickSet::Partial => {change.changes.retain(|hunk| {if let Ok((path, diff)) =pijul_change_to_diff(&diff, hunk, &repo.changes){match to_record::determine_file(&to_record,&path,&changed_files,) {to_record::PickSet::Exclude => {return false;}to_record::PickSet::Partial => {let diff_id = diff::id_parts_hash(&diff);match to_record::determine_change(&path, diff_id, &to_record,) {to_record::Pick::Exclude => {return false;}to_record::Pick::Include => {}}}to_record::PickSet::Include => {}}}true});}to_record::PickSet::Include => {}} - replacement in libflorescence/src/repo.rs at line 859[18.504]→[18.504:527](∅→∅),[9.457]→[4.1758:1815](∅→∅),[18.527]→[4.1758:1815](∅→∅),[8.535]→[4.1758:1815](∅→∅),[2.3019]→[4.1758:1815](∅→∅),[4.1815]→[25.250:999](∅→∅),[9.642]→[2.3081:3113](∅→∅),[25.999]→[2.3081:3113](∅→∅),[4.1988]→[2.3081:3113](∅→∅),[2.3081]→[2.3081:3113](∅→∅),[2.3113]→[4.1989:2013](∅→∅),[4.2013]→[25.1000:1026](∅→∅),[25.1026]→[2.3160:3182](∅→∅),[4.2042]→[2.3160:3182](∅→∅),[2.3160]→[2.3160:3182](∅→∅),[2.3182]→[25.1027:1053](∅→∅),[25.1053]→[9.643:662](∅→∅),[4.2072]→[9.643:662](∅→∅),[9.662]→[25.1054:1464](∅→∅)
match change {BaseHunk::FileMove {del: _,add,path: old_path,} => match add {change::Atom::NewVertex(add) => {let FileMetadata {basename: new_path,metadata: _,..} = FileMetadata::read(&diff.contents[add.start.0.into()..add.end.0.into()],);changes.entry(new_path.to_string()).or_default().insert(ChangedFileDiff::Move {old_path: old_path.clone(),},);}change::Atom::EdgeMap(_edge_map) => todo!(),},BaseHunk::FileDel {del: _,contents,path,encoding,} => {let contents = if let Some(contents) = contents.as_ref() {let raw_contents = get_change_contents(repo_changes,contents,&diff.contents,)?;Some(try_decode_contents(raw_contents, encoding))} else {None};let (path, diff) = pijul_change_to_diff(diff, change, repo_changes)?;changes.entry(path).or_default().insert(diff);}Ok(changes)} - replacement in libflorescence/src/repo.rs at line 865[25.1465]→[9.662:761](∅→∅),[9.662]→[9.662:761](∅→∅),[9.761]→[25.1466:1530](∅→∅),[25.1530]→[9.812:826](∅→∅),[9.812]→[9.812:826](∅→∅),[9.826]→[2.3234:3268](∅→∅),[4.2198]→[2.3234:3268](∅→∅),[2.3234]→[2.3234:3268](∅→∅),[2.3268]→[4.2199:2254](∅→∅),[4.2254]→[2.3317:3339](∅→∅),[2.3317]→[2.3317:3339](∅→∅),[2.3339]→[4.2255:2284](∅→∅),[4.2284]→[9.827:1012](∅→∅),[9.1012]→[2.3391:3423](∅→∅),[4.2412]→[2.3391:3423](∅→∅),[2.3391]→[2.3391:3423](∅→∅),[2.3502]→[2.3502:3524](∅→∅),[2.3524]→[4.2413:2530](∅→∅),[4.2530]→[9.1013:1682](∅→∅),[9.1682]→[2.3712:3741](∅→∅),[4.3028]→[2.3712:3741](∅→∅),[2.3712]→[2.3712:3741](∅→∅),[2.3765]→[2.3765:3788](∅→∅),[2.3788]→[8.536:560](∅→∅),[8.560]→[10.491:517](∅→∅),[10.517]→[8.561:580](∅→∅),[4.3085]→[8.561:580](∅→∅),[8.633]→[8.633:1058](∅→∅),[8.1058]→[10.518:553](∅→∅),[10.553]→[22.7946:8026](∅→∅),[22.8026]→[10.554:630](∅→∅),[8.1202]→[10.554:630](∅→∅),[10.630]→[8.1202:1203](∅→∅),[8.1202]→[8.1202:1203](∅→∅),[8.1203]→[11.179:411](∅→∅)
changes.entry(path.clone()).or_default().insert(ChangedFileDiff::Del { contents });}BaseHunk::FileUndel {undel: _,contents: _,path,encoding: _,} => {changes.entry(path.clone()).or_default().insert(ChangedFileDiff::Undel);}BaseHunk::FileAdd {path,add_name: _,add_inode: _,contents: _,encoding: _,} => {changes.entry(path.clone()).or_default().insert(ChangedFileDiff::Add);}BaseHunk::SolveNameConflict { name: _, path } => {changes.entry(path.clone()).or_default().insert(ChangedFileDiff::SolveNameConflict);}BaseHunk::UnsolveNameConflict { name: _, path } => {changes.entry(path.clone()).or_default().insert(ChangedFileDiff::UnsolveNameConflict);}BaseHunk::Edit {local,change,encoding,} => {let line = local.line;let deleted = if let change::Atom::EdgeMap(map) = change {map.edges.first().map(|edge| edge.flag.is_deleted()).unwrap_or_default()} else {false};// TODO: This fn isn't public in upstream pijul, contriblet raw_contents =get_change_contents(repo_changes, change, &diff.contents)?;let contents = try_decode_contents(raw_contents, encoding);let diff = ChangedFileDiff::Edit {line,deleted,contents,};changes.entry(local.path.clone()).or_default().insert(diff);// Get the file name and diff from a Pijul hunkfn pijul_change_to_diff(diff: &Diff,hunk: &DiffHunk,repo_changes: &FileSystem,) -> anyhow::Result<(String, ChangedFileDiff)> {let changed_file = match hunk {BaseHunk::FileMove {del: _,add,path: old_path,} => match add {change::Atom::NewVertex(add) => {let FileMetadata {basename: new_path,metadata: _,..} = FileMetadata::read(&diff.contents[add.start.0.into()..add.end.0.into()],);(new_path.to_string(),ChangedFileDiff::Move {old_path: old_path.clone(),},) - replacement in libflorescence/src/repo.rs at line 892[8.1461]→[2.3851:3887](∅→∅),[4.3218]→[2.3851:3887](∅→∅),[2.3851]→[2.3851:3887](∅→∅),[2.3887]→[11.412:465](∅→∅),[11.465]→[2.3940:3963](∅→∅),[4.3278]→[2.3940:3963](∅→∅),[2.3940]→[2.3940:3963](∅→∅),[2.3963]→[11.466:492](∅→∅),[11.492]→[9.1819:1838](∅→∅),[4.3308]→[9.1819:1838](∅→∅),[9.1838]→[11.493:574](∅→∅),[11.574]→[22.8027:8107](∅→∅),[22.8107]→[11.687:866](∅→∅),[11.687]→[11.687:866](∅→∅)
BaseHunk::Replacement {change,replacement,local,encoding,} => {let line = local.line;let raw_change_contents =get_change_contents(repo_changes, change, &diff.contents)?;let change_contents =try_decode_contents(raw_change_contents, encoding);let raw_replacement_contents = get_change_contents(change::Atom::EdgeMap(_edge_map) => todo!(),},BaseHunk::FileDel {del: _,contents,path,encoding,} => {let contents = if let Some(contents) = contents.as_ref() {let raw_contents = get_change_contents( - replacement in libflorescence/src/repo.rs at line 903
replacement,contents, - replacement in libflorescence/src/repo.rs at line 906
let replacement_contents =try_decode_contents(raw_replacement_contents, encoding);Some(try_decode_contents(raw_contents, encoding))} else {None}; - replacement in libflorescence/src/repo.rs at line 911[11.1135]→[11.1135:1394](∅→∅),[11.1394]→[9.2002:2268](∅→∅),[9.2002]→[9.2002:2268](∅→∅),[9.2268]→[4.3643:3712](∅→∅),[4.3643]→[4.3643:3712](∅→∅),[4.3712]→[9.2269:2442](∅→∅),[9.2442]→[4.3871:3885](∅→∅),[4.3871]→[4.3871:3885](∅→∅),[4.3885]→[2.4159:4200](∅→∅),[2.4159]→[2.4159:4200](∅→∅),[2.4200]→[4.3886:3913](∅→∅),[4.3913]→[2.4224:4247](∅→∅),[2.4224]→[2.4224:4247](∅→∅),[2.4247]→[4.3914:3943](∅→∅),[4.3943]→[9.2443:3114](∅→∅)
let diff = ChangedFileDiff::Replacement {line,change_contents,replacement_contents,};changes.entry(local.path.clone()).or_default().insert(diff);}BaseHunk::SolveOrderConflict { change: _, local } => {changes.entry(local.path.clone()).or_default().insert(ChangedFileDiff::SolveOrderConflict);}BaseHunk::UnsolveOrderConflict { change: _, local } => {changes.entry(local.path.clone()).or_default().insert(ChangedFileDiff::UnsolveOrderConflict);}BaseHunk::ResurrectZombies {change: _,local,encoding: _,} => {changes.entry(local.path.clone()).or_default().insert(ChangedFileDiff::ResurrectZombines);}BaseHunk::AddRoot { name: _, inode: _ } => {changes.entry(ROOT_FILE.to_string()).or_default().insert(ChangedFileDiff::AddRoot);}BaseHunk::DelRoot { name: _, inode: _ } => {changes.entry(ROOT_FILE.to_string()).or_default().insert(ChangedFileDiff::DelRoot);}(path.clone(), ChangedFileDiff::Del { contents }) - replacement in libflorescence/src/repo.rs at line 913
}Ok(changes)BaseHunk::FileUndel {undel: _,contents: _,path,encoding: _,} => (path.clone(), ChangedFileDiff::Undel),BaseHunk::FileAdd {path,add_name: _,add_inode: _,contents: _,encoding: _,} => (path.clone(), ChangedFileDiff::Add),BaseHunk::SolveNameConflict { name: _, path } => {(path.clone(), ChangedFileDiff::SolveNameConflict)}BaseHunk::UnsolveNameConflict { name: _, path } => {(path.clone(), ChangedFileDiff::UnsolveNameConflict)}BaseHunk::Edit {local,change,encoding,} => {let line = local.line;let deleted = if let change::Atom::EdgeMap(map) = change {map.edges.first().map(|edge| edge.flag.is_deleted()).unwrap_or_default()} else {false};// TODO: This fn isn't public in upstream pijul, contriblet raw_contents =get_change_contents(repo_changes, change, &diff.contents)?;let contents = try_decode_contents(raw_contents, encoding);let diff = ChangedFileDiff::Edit {line,deleted,contents,};(local.path.clone(), diff)}BaseHunk::Replacement {change,replacement,local,encoding,} => {let line = local.line;let raw_change_contents =get_change_contents(repo_changes, change, &diff.contents)?;let change_contents =try_decode_contents(raw_change_contents, encoding);let raw_replacement_contents =get_change_contents(repo_changes, replacement, &diff.contents)?;let replacement_contents =try_decode_contents(raw_replacement_contents, encoding);let diff = ChangedFileDiff::Replacement {line,change_contents,replacement_contents,};(local.path.clone(), diff)}BaseHunk::SolveOrderConflict { change: _, local } => {(local.path.clone(), ChangedFileDiff::SolveOrderConflict)}BaseHunk::UnsolveOrderConflict { change: _, local } => {(local.path.clone(), ChangedFileDiff::UnsolveOrderConflict)}BaseHunk::ResurrectZombies {change: _,local,encoding: _,} => (local.path.clone(), ChangedFileDiff::ResurrectZombines),BaseHunk::AddRoot { name: _, inode: _ } => {(ROOT_FILE.to_string(), ChangedFileDiff::AddRoot)}BaseHunk::DelRoot { name: _, inode: _ } => {(ROOT_FILE.to_string(), ChangedFileDiff::DelRoot)}};Ok(changed_file) - replacement in libflorescence/src/repo/test.rs at line 116
repo::record(&internal, msg.to_string(), None, skey).unwrap();let to_record = crate::to_record::State::default();repo::record(&internal, msg.to_string(), None, skey, to_record).unwrap(); - edit in libflorescence/src/lib.rs at line 10
pub mod to_record; - replacement in inflorescence_view/src/diff.rs at line 106
to_record::determine_change_pick(path, *diff_id, to_record);to_record::determine_change(path, *diff_id, to_record); - edit in inflorescence_model/src/to_record.rs at line 6
pub use libflorescence::to_record::{determine_change, determine_file, PartialFile, Pick, PickSet, State,}; - edit in inflorescence_model/src/to_record.rs at line 11
use libflorescence::to_record::{default_pick, default_pick_set, file_has_any_partial_change,}; - edit in inflorescence_model/src/to_record.rs at line 16
use std::collections::HashMap; - edit in inflorescence_model/src/to_record.rs at line 21[26.6019]→[23.2854:2857](∅→∅),[23.2854]→[23.2854:2857](∅→∅),[23.2857]→[24.1889:1934](∅→∅),[24.1934]→[23.2887:2932](∅→∅),[23.2887]→[23.2887:2932](∅→∅),[23.2932]→[24.1935:1989](∅→∅),[24.1989]→[23.2971:3774](∅→∅),[23.2971]→[23.2971:3774](∅→∅),[23.3774]→[26.6020:6066](∅→∅),[26.6066]→[23.3829:4378](∅→∅),[23.3829]→[23.3829:4378](∅→∅),[23.4378]→[26.6067:6768](∅→∅)
}#[derive(Clone, Copy, Debug, PartialEq, Eq)]pub enum Pick {Include,Exclude,}#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]pub enum PickSet {#[default]Include,Exclude,Partial,}#[derive(Clone, Debug, Default)]pub struct State {/// State of all filespub overall: PickSet,/// Map from file name to its changes state. This is preserved even when/// `overall` selection changes.pub files: HashMap<String, PickSet>,/// Map from file name to its individual change state. This is preserved/// even when `overall` and `files` selection changespub changes: HashMap<String, PartialFile>,}#[derive(Clone, Debug, Default)]pub struct PartialFile {/// Map from file name to its state// NOTE: When it's possible to select individual lines/chars to record,// then the value would be `PickSet` and the inner-most value of// selected ranges would be `Pick`pub changes: HashMap<diff::IdHash, Pick>,}pub fn determine_file(state: &State,file: &str,changed_files: &repo::ChangedFiles,) -> PickSet {match state.overall {PickSet::Include => PickSet::Include,PickSet::Exclude => PickSet::Exclude,PickSet::Partial => {if let Some(pick) = state.files.get(file) {*pick} else if file_has_any_partial_change(file, state, changed_files) {PickSet::Partial} else {default_pick_set(state)}}}}pub fn determine_change_pick(file: &str,diff_id: diff::IdHash,state: &State,) -> Pick {match state.overall {PickSet::Include => return Pick::Include,PickSet::Exclude => return Pick::Exclude,PickSet::Partial => {}}if let Some(pick) = state.files.get(file) {match pick {PickSet::Include => return Pick::Include,PickSet::Exclude => return Pick::Exclude,PickSet::Partial => {}}}let explicit = state.changes.get(file).and_then(|file| file.changes.get(&diff_id)).copied();dbg!(&explicit);explicit.unwrap_or_else(|| default_pick(state)) - edit in inflorescence_model/src/to_record.rs at line 186
}}/// Determine the default value for a file that is not explicitly set.fn default_pick_set(state: &State) -> PickSet {match default_pick(state) {Pick::Include => PickSet::Include,Pick::Exclude => PickSet::Exclude, - edit in inflorescence_model/src/to_record.rs at line 189[23.7771]→[23.7771:7933](∅→∅),[23.7933]→[24.4901:4921](∅→∅),[24.4921]→[23.7950:8125](∅→∅),[23.7950]→[23.7950:8125](∅→∅),[23.8287]→[23.8287:8359](∅→∅)
/// Determine the default value for a change that is not explicitly set.fn default_pick(state: &State) -> Pick {let State {overall,files,changes: _,} = state;if matches!(overall, PickSet::Partial)|| files.values().any(|pick| matches!(pick, PickSet::Partial | PickSet::Exclude)){Pick::Exclude} else {Pick::Include}} - edit in inflorescence_model/src/to_record.rs at line 212
fn file_has_any_partial_change(file: &str,state: &State,changed_files: &repo::ChangedFiles,) -> bool {let changes = changed_files.get(file).unwrap();let (mut has_include, mut has_exclude) = (false, false);state.changes.get(file).map(|file| {if file.changes.len() == changes.len() {// The number of of changes in selection is the same as number// of actual changes, we need to have both exclude and include// to be partialfile.changes.values().any(|pick| {has_exclude |= matches!(pick, Pick::Exclude);has_include |= matches!(pick, Pick::Include);has_include && has_exclude})} else {// There is the same number of changes in selection is not the// same as number of actual changes, we only need to find 1// exclusion to be partialfile.changes.values().any(|pick| matches!(pick, Pick::Exclude))}}).unwrap_or_default()} - edit in inflorescence/src/main.rs at line 917
to_record, - replacement in inflorescence/src/main.rs at line 939
.send(repo::MsgIn::Record { msg, desc, sk }).send(repo::MsgIn::Record {msg,desc,sk,to_record: to_record.clone(),})