allow to initiate a new repo

tzemanovic
Feb 10, 2026, 9:10 PM
IOXNOVX2FJWPCEFVUHA5FEKIZJQT45R7VBAA43EPAKKVF5IWF4GAC

Dependencies

  • [2] 6YZAVBWU Initial commit
  • [3] KT5UYXGK fix selection after adding file, add changed file diffs
  • [4] ELG3UDT6 allow to rm added files
  • [5] A5YBC77V record!
  • [6] HOJZI52Y rename flowers_ui to inflorescence
  • [7] ONRCENKT rm unnecessary state from repo's internal state
  • [8] VCNKFNUF app init test
  • [9] ACDXXAX2 refactor main's updates into smaller fns
  • [10] WXQBBQ2A update nightly
  • [11] JZXYSIYD channel selection!
  • [12] 5ZRDYL6K fork channel, fix recording esc key
  • [13] 3XRG4BB6 rewritten nav-scrollable!
  • [14] WAOGSCOJ very nice refactor, wip adding channels logs
  • [15] EJPSD5XO shared allowed actions conditions between update and view
  • [16] YK3MOJJL chonky refactor, wip other channels logs & diffs
  • [17] 7WCB5YQJ refactor msgs and modules
  • [18] AZ5D2LQU allow to set record description
  • [19] IFQPVMBD error handling for repo actions
  • [20] TEDT26JQ add push and pull sub-menus
  • [21] BJ3CYLUT allow to reset changed file
  • [22] FTRAPDYA add simple cli with repo path arg
  • [23] YRGDFHAB project dir picker
  • [24] LPSUBGUB add projects picker
  • [25] MORKDJUE use allowed actions binding for key subs
  • [26] OLDN7R34 retry upsert project
  • [27] 5BAPU7K6 dir picker key navigation
  • [28] TMDH7GPV dir picker scrollables handling + confirmation
  • [29] SEJXDXPW improve file watcher to respect .ignore file
  • [30] 47ZTC4WS fix forking during channel selection
  • [31] YGZ3VCW4 add push
  • [32] 23SFYK4Q big view refactor into a new crate
  • [33] C3OS2JJ6 clear cache on saving record & refresh
  • [34] ODCT4QJN add pull
  • [35] S4WH75Y3 allow to select channels if there are any other. Conditional switch
  • [36] W7IUT3ZV start recording impl
  • [37] AMPZ2BXK show changed files diffs (only Edit atm)
  • [38] PTWZYQFR use nav-scrollable for repo status
  • [39] WT3GA27P add cursor with selection
  • [40] KM5PSZ4A watch repo once loaded
  • [41] UR4J677R nav for log changes and refactors
  • [42] WH57EHNM update tests
  • [43] PKLUHYE4 allow to copy change hash
  • [44] D7A7MSIH allow to defer or abandon record, add buttons
  • [45] BJXUYQ2Y show untracked file contents in read-only text editor
  • [46] FU6P5QLG indicate when a file is a dir with appended '/'
  • [*] SWWE2R6M display basic repo stuff

Change contents

  • edit in libflorescence/src/repo.rs at line 97
    [14.641]
    [14.641]
    /// Initialized `manage` task
  • edit in libflorescence/src/repo.rs at line 534
    [7.1392]
    [7.1566]
    }
    pub async fn init(path: PathBuf) -> Result<PathBuf, anyhow::Error> {
    let config =
    PijulConfig::load(None, vec![]).context("Loading Pijul config")?;
    let repo = pijul::Repository::init(&config, Some(&path), None, None)
    .with_context(|| {
    format!("Initializing a new Pijul repo at {}", path.display())
    })?;
    let mut txn = repo.pristine.mut_txn_begin()?;
    let channel_name = libpijul::DEFAULT_CHANNEL.to_string();
    txn.open_or_create_channel(&channel_name)
    .context("Creating a default channel")?;
    txn.set_current_channel(&channel_name)
    .context("Setting the current channel to the default channel")?;
    txn.commit().context("Commit")?;
    Ok(path)
  • replacement in libflorescence/src/repo.rs at line 630
    [19.3935][19.3935:3954]()
    txn.commit()?;
    [19.3935]
    [19.3954]
    txn.commit().context("Commit")?;
  • replacement in libflorescence/src/repo.rs at line 642
    [19.4307][19.4307:4326]()
    txn.commit()?;
    [19.4307]
    [19.4326]
    txn.commit().context("Commit")?;
  • replacement in libflorescence/src/repo.rs at line 856
    [5.3983][19.6021:6040]()
    txn.commit()?;
    [5.3983]
    [19.6040]
    txn.commit().context("Commit")?;
  • replacement in libflorescence/src/repo.rs at line 957
    [4.155][19.7334:7353]()
    txn.commit()?;
    [4.155]
    [19.7353]
    txn.commit().context("Commit")?;
  • replacement in libflorescence/src/repo.rs at line 970
    [3.1670][19.7572:7591]()
    txn.commit()?;
    [3.1670]
    [19.7591]
    txn.commit().context("Commit")?;
  • replacement in libflorescence/src/repo.rs at line 1633
    [21.1734][21.1734:1753]()
    txn.commit()?;
    [21.1734]
    [21.1753]
    txn.commit().context("Commit")?;
  • replacement in inflorescence_model/src/model.rs at line 75
    [23.3592383][25.170878:170929]()
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
    [23.3592383]
    [20.531929]
    #[derive(Clone, Debug, PartialEq, Eq, Hash)]
  • edit in inflorescence_model/src/model.rs at line 80
    [21.1829]
    [20.531968]
    InitRepo { path: PathBuf },
  • replacement in inflorescence_model/src/action.rs at line 185
    [23.3592527][23.3592527:3592591]()
    let mut bindings = if let Some(sub_menu) = state.sub_menu {
    [23.3592527]
    [20.532218]
    let mut bindings = if let Some(sub_menu) = &state.sub_menu {
  • edit in inflorescence_model/src/action.rs at line 232
    [20.532531]
    [20.532531]
    model::SubMenu::InitRepo { path: _ } => {
    vec![confirm("confirm init repo"), cancel()]
    }
  • edit in inflorescence_iced_widget/src/dir_picker.rs at line 114
    [23.3116]
    [23.3116]
    /// Picked an existing Pijul or Git Repo
  • edit in inflorescence_iced_widget/src/dir_picker.rs at line 116
    [23.3137]
    [23.3137]
    /// Create a new project in non-existing directory
    CreateNew(PathBuf),
  • edit in inflorescence_iced_widget/src/dir_picker.rs at line 571
    [28.10032]
    [28.10032]
    action = Some(Action::CreateNew(
    state
    .current_dir
    .as_path()
    .to_path_buf()
    .join(&state.input),
    ));
  • edit in inflorescence/src/main.rs at line 107
    [26.422]
    [23.3609686]
    InitedRepo(anyhow::Result<PathBuf>),
  • edit in inflorescence/src/main.rs at line 264
    [29.1262]
    [29.1262]
    &mut state.model.sub_menu,
    &mut state.model.report,
  • edit in inflorescence/src/main.rs at line 267
    [29.1287][29.1287:1332]()
    &mut state.model.report,
  • edit in inflorescence/src/main.rs at line 284
    [23.3612856][23.3612856:3612896]()
    .map(Msg::ManagingRepo)
  • edit in inflorescence/src/main.rs at line 381
    [24.20458]
    [23.3613417]
    Msg::InitedRepo(result) => {
    let task = match result {
    Ok(path) => {
    let (sub, managing_repo, managing_repo_task) =
    init_managing_repo(
    path.clone(),
    &mut state.model.report,
    );
    state.model.sub = model::SubState::ManagingRepo(sub);
    state.managing_repo = Some(managing_repo);
    managing_repo_task
    }
    Err(err) => {
    let msg =
    format!("Failed to initialize a new repo: {err:?}");
    report::show_err(&mut state.model.report, msg);
    Task::none()
    }
    };
    state.model.sub_menu = None;
    task
    }
  • replacement in inflorescence/src/main.rs at line 771
    [23.3615357][23.3615357:3615377]()
    msg: view::Msg,
    [23.3615357]
    [29.3356]
    sub_menu: &mut Option<model::SubMenu>,
  • edit in inflorescence/src/main.rs at line 773
    [29.3392]
    [23.3615377]
    msg: view::Msg,
  • replacement in inflorescence/src/main.rs at line 778
    [28.11404][28.11404:12086]()
    let mut handle_action = |action| {
    if let Some(dir_picker::Action::Picked(dir)) = action {
    // If it contains Pijul repo, init ManagingRepo state
    // TODO: If it contains Git, offer to migrate it
    // TODO: Otherwise, offer to init Pijul from scratch
    let project = store::Project {
    last_closed_time: cmp::Reverse(None),
    path: dir.clone(),
    };
    let store_project_task = if repo::is_pijul(&project.path) {
    Task::perform(
    async move { store::upsert_project(project).await },
    Msg::UpsertProjectResult,
    )
    [28.11404]
    [28.12086]
    let mut handle_action =
    |sub_menu: &mut Option<model::SubMenu>,
    action: Option<dir_picker::Action>| {
    if let Some(action) = action {
    match action {
    dir_picker::Action::Picked(dir) => {
    // If it contains Pijul repo, init ManagingRepo state
    // TODO: If it contains Git, offer to migrate it
    // TODO: Otherwise, offer to init Pijul from scratch
    let project = store::Project {
    last_closed_time: cmp::Reverse(None),
    path: dir.clone(),
    };
    let store_project_task =
    if repo::is_pijul(&project.path) {
    Task::perform(
    async move {
    store::upsert_project(project).await
    },
    Msg::UpsertProjectResult,
    )
    } else {
    Task::none()
    };
    let (sub, managing_repo, managing_repo_task) =
    init_managing_repo(dir, report);
    new_state = Some((sub, managing_repo));
    Task::batch([managing_repo_task, store_project_task])
    }
    dir_picker::Action::CreateNew(path) => {
    *sub_menu = Some(model::SubMenu::InitRepo { path });
    Task::none()
    }
    }
  • replacement in inflorescence/src/main.rs at line 814
    [28.12136][28.12136:12210](),[28.12210][29.3393:3442](),[29.3442][28.12251:12428](),[28.12251][28.12251:12428]()
    };
    let (sub, managing_repo, managing_repo_task) =
    init_managing_repo(dir, report);
    new_state = Some((sub, managing_repo));
    Task::batch([managing_repo_task, store_project_task])
    } else {
    Task::none()
    }
    };
    [28.12136]
    [28.12428]
    }
    };
  • replacement in inflorescence/src/main.rs at line 827
    [27.18160][28.12485:12877]()
    action::FilteredMsg::Confirm => {
    let (task, action) =
    dir_picker::confirm_input_or_selection(picker);
    let dir_picker_task =
    task.map(view::Msg::PickingRepoDir).map(Msg::View);
    let action_task = handle_action(action);
    Task::batch([dir_picker_task, action_task])
    }
    [27.18160]
    [28.12877]
    action::FilteredMsg::Confirm => match &sub_menu {
    Some(model::SubMenu::InitRepo { path }) => {
    let path = path.clone();
    Task::perform(
    async move { repo::init(path).await },
    Msg::InitedRepo,
    )
    }
    Some(
    model::SubMenu::Push
    | model::SubMenu::Pull
    | model::SubMenu::ResetChange,
    )
    | None => {
    let (task, action) =
    dir_picker::confirm_input_or_selection(picker);
    let dir_picker_task =
    task.map(view::Msg::PickingRepoDir).map(Msg::View);
    let action_task = handle_action(sub_menu, action);
    Task::batch([dir_picker_task, action_task])
    }
    },
  • replacement in inflorescence/src/main.rs at line 874
    [24.22469][28.12918:12971]()
    let action_task = handle_action(action);
    [24.22469]
    [28.12971]
    let action_task = handle_action(sub_menu, action);
  • replacement in inflorescence/src/main.rs at line 907
    [23.3616929][23.3616929:3616958]()
    ) -> Task<ManagingRepoMsg> {
    [23.3616929]
    [15.24444]
    ) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 920
    [23.3617127][23.3617127:3617281]()
    view::Msg::EditRecordMsg(action) => edit_record_msg(model, action),
    view::Msg::EditRecordDesc(action) => edit_record_desc(model, action),
    [23.3617127]
    [17.12019]
    view::Msg::EditRecordMsg(action) => {
    edit_record_msg(model, action).map(Msg::ManagingRepo)
    }
    view::Msg::EditRecordDesc(action) => {
    edit_record_desc(model, action).map(Msg::ManagingRepo)
    }
  • edit in inflorescence/src/main.rs at line 953
    [17.12593]
    [17.12593]
    .map(Msg::ManagingRepo)
  • replacement in inflorescence/src/main.rs at line 979
    [17.12734][23.3617616:3617645]()
    ) -> Task<ManagingRepoMsg> {
    [17.12734]
    [16.60264]
    ) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 981
    [16.60291][17.12752:12794](),[17.12794][23.3617646:3618011](),[21.3004][20.533909:533971](),[23.3618011][20.533909:533971](),[20.533909][20.533909:533971](),[20.533971][14.83003:83140](),[14.83003][14.83003:83140](),[14.83140][23.3618012:3618056](),[18.10354][14.83190:83204](),[16.60345][14.83190:83204](),[23.3618056][14.83190:83204](),[14.83190][14.83190:83204](),[14.83204][12.5950:5999](),[12.5950][12.5950:5999](),[12.5999][30.1127:1840](),[30.1840][14.83283:83479](),[14.83283][14.83283:83479]()
    action::FilteredMsg::Confirm => {
    if let Some(menu) = &sub_menu {
    match menu {
    model::SubMenu::Push => push(state, model, sub_menu),
    model::SubMenu::Pull => pull(state, model, sub_menu),
    model::SubMenu::ResetChange => {
    reset_change(state, model, sub_menu, report)
    }
    }
    } else if let Some(ReadyState {
    repo,
    navigation: _,
    selection,
    forking_channel_name,
    ..
    }) = model::is_ready_mut(model)
    {
    #[allow(clippy::collapsible_if)]
    if let Some(name) = forking_channel_name.take_if(|name| {
    let name = name.trim();
    let empty = name.is_empty();
    let unique = || {
    repo.channel != name
    && !repo.other_channels.iter().any(|n| n == name)
    };
    !empty && unique()
    }) {
    state
    .repo_tx_in
    .send(repo::MsgIn::ForkChannel(name))
    .unwrap();
    } else if matches!(
    selection.primary,
    selection::Primary::Channel
    ) {
    if let Some(selection::Channel {
    ix: _,
    name,
    log: _,
    }) = selection.channel.take()
    [16.60291]
    [12.6173]
    action::FilteredMsg::Confirm => match &sub_menu {
    Some(model::SubMenu::Push) => push(state, model, sub_menu),
    Some(model::SubMenu::Pull) => pull(state, model, sub_menu),
    Some(model::SubMenu::ResetChange) => {
    reset_change(state, model, sub_menu, report)
    }
    Some(model::SubMenu::InitRepo { .. }) | None => {
    if let Some(ReadyState {
    repo,
    navigation: _,
    selection,
    forking_channel_name,
    ..
    }) = model::is_ready_mut(model)
    {
    #[allow(clippy::collapsible_if)]
    if matches!(selection.primary, selection::Primary::Channel)
    {
    if let Some(selection::Channel {
    ix: _,
    name,
    log: _,
    }) = selection.channel.take()
    {
    state
    .repo_tx_in
    .send(repo::MsgIn::SwitchToChannel(name))
    .unwrap();
    selection.primary = selection::Primary::default();
    }
    } else if let Some(name) =
    forking_channel_name.take_if(|name| {
    let name = name.trim();
    let empty = name.is_empty();
    let unique = || {
    repo.channel != name
    && !repo
    .other_channels
    .iter()
    .any(|n| n == name)
    };
    !empty && unique()
    })
  • replacement in inflorescence/src/main.rs at line 1028
    [12.6265][12.6265:6335]()
    .send(repo::MsgIn::SwitchToChannel(name))
    [12.6265]
    [12.6335]
    .send(repo::MsgIn::ForkChannel(name))
  • edit in inflorescence/src/main.rs at line 1030
    [12.6374][11.18500:18501](),[11.18500][11.18500:18501](),[11.18501][14.83480:83555]()
    selection.primary = selection::Primary::default();
  • edit in inflorescence/src/main.rs at line 1031
    [12.6453]
    [12.6791]
    Task::none()
    } else {
    Task::none()
  • edit in inflorescence/src/main.rs at line 1035
    [12.6809][20.533972:534051]()
    Task::none()
    } else {
    Task::none()
  • replacement in inflorescence/src/main.rs at line 1036
    [11.18588][11.18588:18598]()
    }
    [11.18563]
    [17.12795]
    },
  • edit in inflorescence/src/main.rs at line 1086
    [16.60508]
    [16.60508]
    .map(Msg::ManagingRepo)
  • replacement in inflorescence/src/main.rs at line 1335
    [23.3619516][23.3619516:3619545]()
    ) -> Task<ManagingRepoMsg> {
    [23.3619516]
    [23.3619545]
    ) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1355
    [23.3619766][23.3619766:3619795]()
    ) -> Task<ManagingRepoMsg> {
    [23.3619766]
    [23.3619795]
    ) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1376
    [23.3620060][23.3620060:3620089]()
    ) -> Task<ManagingRepoMsg> {
    [23.3620060]
    [23.3620089]
    ) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1405
    [21.3937][23.3620209:3620287]()
    fn clipboard_copy(model: &mut model::ManagingRepo) -> Task<ManagingRepoMsg> {
    [21.3937]
    [23.3620287]
    fn clipboard_copy(model: &mut model::ManagingRepo) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1574
    [23.3622368][23.3622368:3622397]()
    ) -> Task<ManagingRepoMsg> {
    [23.3622368]
    [14.86594]
    ) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1638
    [14.87495][13.20384:20419](),[11.19782][13.20384:20419]()
    return selection_task;
    [14.87495]
    [10.910]
    return selection_task.map(Msg::ManagingRepo);
  • replacement in inflorescence/src/main.rs at line 1650
    [23.3622559][23.3622559:3622588]()
    ) -> Task<ManagingRepoMsg> {
    [23.3622559]
    [14.87496]
    ) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1725
    [14.88495][13.20518:20557](),[11.20141][13.20518:20557]()
    return selection_task;
    [14.88495]
    [10.2022]
    return selection_task.map(Msg::ManagingRepo);
  • replacement in inflorescence/src/main.rs at line 1734
    [9.3730][23.3622686:3622762]()
    fn start_record(model: &mut model::ManagingRepo) -> Task<ManagingRepoMsg> {
    [9.3730]
    [14.88496]
    fn start_record(model: &mut model::ManagingRepo) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1796
    [23.3623133][23.3623133:3623162]()
    ) -> Task<ManagingRepoMsg> {
    [23.3623133]
    [14.88980]
    ) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1849
    [9.5836][23.3623200:3623276]()
    fn defer_record(model: &mut model::ManagingRepo) -> Task<ManagingRepoMsg> {
    [9.5836]
    [23.3623276]
    fn defer_record(model: &mut model::ManagingRepo) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1868
    [9.6184][23.3623357:3623435]()
    fn abandon_record(model: &mut model::ManagingRepo) -> Task<ManagingRepoMsg> {
    [9.6184]
    [18.13461]
    fn abandon_record(model: &mut model::ManagingRepo) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1880
    [9.6363][23.3623473:3623547]()
    fn focus_next(model: &mut model::ManagingRepo) -> Task<ManagingRepoMsg> {
    [9.6363]
    [18.13705]
    fn focus_next(model: &mut model::ManagingRepo) -> Task<Msg> {
  • replacement in inflorescence/src/main.rs at line 1893
    [18.13983][23.3623585:3623659]()
    fn focus_prev(model: &mut model::ManagingRepo) -> Task<ManagingRepoMsg> {
    [18.13983]
    [18.14031]
    fn focus_prev(model: &mut model::ManagingRepo) -> Task<Msg> {
  • edit in inflorescence/Cargo.toml at line 27
    [2.3680]
    [22.727]
    [dependencies.anyhow]
    workspace = true
  • edit in Cargo.lock at line 2710
    [6.391]
    [8.4865]
    "anyhow",