diff options
| author | Mica White <botahamec@outlook.com> | 2026-03-28 20:33:49 -0400 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2026-03-28 20:33:49 -0400 |
| commit | 6d73f96706814e58d03761ae997972a6551c081e (patch) | |
| tree | 272f4dbb898c2a89daddc07e53c5bea7ce47a1ea | |
| parent | 2f71b45a753ff80cd9d7710be47b6777ed43e807 (diff) | |
MVP autosave command
| -rw-r--r-- | Cargo.lock | 428 | ||||
| -rw-r--r-- | Cargo.toml | 5 | ||||
| -rw-r--r-- | rustfmt.toml | 1 | ||||
| -rw-r--r-- | src/lib.rs | 141 |
4 files changed, 481 insertions, 94 deletions
@@ -3,35 +3,27 @@ version = 4 [[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] name = "anyhow" version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] -name = "bitflags" -version = "2.11.0" +name = "auth-git2" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "4888bf91cce63baf1670512d0f12b5d636179a4abbad6504812ac8ab124b3efe" +dependencies = [ + "dirs", + "git2", + "terminal-prompt", +] [[package]] -name = "bstr" -version = "1.12.1" +name = "bitflags" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" -dependencies = [ - "memchr", - "serde", -] +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bumpalo" @@ -71,29 +63,83 @@ dependencies = [ ] [[package]] -name = "crossbeam-deque" -version = "0.8.6" +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", ] [[package]] -name = "crossbeam-epoch" -version = "0.9.18" +name = "dirs" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "crossbeam-utils", + "dirs-sys", ] [[package]] -name = "crossbeam-utils" -version = "0.8.21" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] [[package]] name = "displaydoc" @@ -107,12 +153,37 @@ dependencies = [ ] [[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] name = "etcetera" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -145,6 +216,26 @@ dependencies = [ ] [[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] name = "getrandom" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -174,10 +265,13 @@ name = "git-autosave" version = "0.1.0" dependencies = [ "anyhow", + "auth-git2", "confy", "git2", - "ignore", + "hostname", + "inquire", "serde", + "thiserror", "uuid", ] @@ -197,19 +291,6 @@ dependencies = [ ] [[package]] -name = "globset" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -240,6 +321,17 @@ dependencies = [ ] [[package]] +name = "hostname" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" +dependencies = [ + "cfg-if", + "libc", + "windows-link", +] + +[[package]] name = "icu_collections" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -348,22 +440,6 @@ dependencies = [ ] [[package]] -name = "ignore" -version = "0.4.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - -[[package]] name = "indexmap" version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -376,6 +452,20 @@ dependencies = [ ] [[package]] +name = "inquire" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6654738b8024300cf062d04a1c13c10c8e2cea598ec1c47dc9b6641159429756" +dependencies = [ + "bitflags", + "crossterm", + "dyn-clone", + "fuzzy-matcher", + "unicode-segmentation", + "unicode-width", +] + +[[package]] name = "itoa" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -434,6 +524,15 @@ dependencies = [ ] [[package]] +name = "libredox" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +dependencies = [ + "libc", +] + +[[package]] name = "libssh2-sys" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -460,12 +559,33 @@ dependencies = [ ] [[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] name = "litemap" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -478,6 +598,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -502,6 +634,35 @@ dependencies = [ ] [[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -563,21 +724,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] -name = "regex-automata" -version = "0.4.14" +name = "redox_syscall" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", ] [[package]] -name = "regex-syntax" -version = "0.8.10" +name = "rustix" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] [[package]] name = "rustversion" @@ -586,13 +772,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "same-file" -version = "1.0.6" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" @@ -659,6 +842,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -693,6 +907,16 @@ dependencies = [ ] [[package]] +name = "terminal-prompt" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572818b3472910acbd5dff46a3413715c18e934b071ab2ba464a7b2c2af16376" +dependencies = [ + "libc", + "winapi", +] + +[[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -713,6 +937,15 @@ dependencies = [ ] [[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] name = "tinystr" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -768,6 +1001,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -809,14 +1054,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] -name = "walkdir" -version = "2.5.0" +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" @@ -916,15 +1157,28 @@ dependencies = [ ] [[package]] -name = "winapi-util" -version = "0.1.11" +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "windows-sys 0.61.2", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -7,6 +7,9 @@ edition = "2024" git2 = "0.20" serde = { version = "1.0.152", features = ["derive"] } uuid = { version = "1", features = ["v4"] } +thiserror = "2" anyhow = "1" confy = "2" -ignore = "0.4" +hostname = "0.4" +inquire = "0.9" +auth-git2 = "0.5" diff --git a/rustfmt.toml b/rustfmt.toml index 3a79d0e..d3d0d1e 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,2 +1,3 @@ +edition = "2024" hard_tabs = true newline_style = "Unix" @@ -39,11 +39,13 @@ * - allow force */ -use std::path::PathBuf; +use std::fs::Metadata; +use std::path::{Path, PathBuf}; use confy::ConfyError; -use git2::{Error, Repository}; +use git2::{Oid, PushOptions, RemoteCallbacks, Repository}; use serde::{Deserialize, Serialize}; +use thiserror::Error; use uuid::Uuid; #[derive(Default, Serialize, Deserialize)] @@ -51,6 +53,14 @@ pub struct Config { repositories: Vec<PathBuf>, } +#[derive(Debug, Error)] +pub enum TreeError { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Git(#[from] git2::Error), +} + pub fn load_config() -> Result<Config, ConfyError> { confy::load("git-autosave", "git-autosaved") } @@ -59,14 +69,133 @@ pub fn save_config(config: &Config) -> Result<(), ConfyError> { confy::store("git-autosave", "git-autosaved", config) } -pub fn init(repository: &Repository, config: Option<&mut Config>) -> Result<(), Error> { +pub fn init(repository: &Repository, config: Option<&mut Config>) -> Result<(), git2::Error> { let id = Uuid::new_v4(); let workdir = repository.workdir(); - repository.config()?.set_str("autosave.id", &id.to_string())?; + repository + .config()? + .set_str("autosave.id", &id.to_string())?; - if let Some(config) = config && let Some(workdir) = workdir { + if let Some(config) = config + && let Some(workdir) = workdir + { config.repositories.push(workdir.into()); } - + + Ok(()) +} + +pub fn current_branch_ref(repository: &Repository) -> Result<String, git2::Error> { + let head = repository.head()?; + let ref_name = match head.kind() { + Some(git2::ReferenceType::Symbolic) => head.symbolic_target(), + _ => head.name(), + } + .ok_or(git2::Error::new( + git2::ErrorCode::Invalid, + git2::ErrorClass::Reference, + "The current branch's name is not valid UTF-8", + ))? + .to_string(); + Ok(ref_name) +} + +pub fn current_branch(repository: &Repository) -> Result<String, git2::Error> { + let ref_name = current_branch_ref(repository)?; + let branch_name = ref_name + .strip_prefix("refs/heads/") + .unwrap_or(&ref_name) + .to_string(); + Ok(branch_name) +} + +pub fn filemode_for_dir_entry(metadata: &Metadata) -> i32 { + if metadata.is_dir() { + 0o040000 + } else if metadata.is_symlink() { + 0o120000 + } else if metadata.permissions().readonly() { + 0o100644 + } else { + 0o100755 + } +} + +pub fn path_to_tree(repository: &Repository, path: &Path) -> Result<Oid, TreeError> { + let workdir = repository.workdir().expect("a non-bare repo"); + if path.is_dir() { + let mut tree = repository.treebuilder(None)?; + for entry in path.read_dir()? { + let entry = entry?; + let full_path = entry.path(); + let relative_path = full_path.strip_prefix(workdir).unwrap_or(path); + if repository.is_path_ignored(relative_path)? { + continue; + } + + let filemode = filemode_for_dir_entry(&entry.metadata()?); + let oid = path_to_tree(repository, &entry.path())?; + let filename = entry.file_name(); + tree.insert(filename, oid, filemode)?; + } + Ok(tree.write()?) + } else { + Ok(repository.blob_path(path)?) + } +} + +pub fn workdir_to_tree(repository: &Repository) -> Result<Oid, TreeError> { + let Some(workdir) = repository.workdir() else { + return Err(TreeError::Git(git2::Error::new( + git2::ErrorCode::BareRepo, + git2::ErrorClass::Repository, + "git-autosave only supports worktrees", + ))); + }; + + path_to_tree(repository, workdir) +} + +pub fn commit_autosave(repository: &Repository) -> Result<Oid, TreeError> { + let config = repository.config()?; + let head = repository.head()?; + let uid = config.get_entry("autosave.id")?; + let uid = String::from_utf8_lossy(uid.value_bytes()); + let branch = current_branch(repository)?; + let refname = &format!("refs/autosave/autosaves/{uid}/{branch}"); + let signature = repository.signature()?; + let tree = repository.find_tree(workdir_to_tree(repository)?)?; + let parent = head.peel_to_commit()?; + let hostname = hostname::get(); + let hostname = hostname + .map(|hostname| hostname.to_string_lossy().into_owned()) + .map(|hostname| format!("Autosave: {hostname}")) + .unwrap_or_else(|_| "Autosave".to_string()); + + let commit = repository.commit(None, &signature, &signature, &hostname, &tree, &[&parent])?; + repository.reference(refname, commit, true, "Autosaved {branch}")?; + + Ok(commit) +} + +pub fn push_autosaves( + repository: &Repository, + callbacks: RemoteCallbacks<'_>, +) -> Result<(), git2::Error> { + let remote = repository.branch_upstream_remote(¤t_branch_ref(repository)?)?; + let mut remote = repository.find_remote(&String::from_utf8_lossy(&remote))?; + let refs = repository + .references()? + .filter_map(|reference| reference.ok()) + .filter(|reference| { + reference + .name_bytes() + .starts_with(b"refs/autosave/autosaves") + }) + .filter_map(|reference| reference.name().map(|n| n.to_string())) + .map(|reference| format!("+{reference}:{reference}")) + .collect::<Vec<_>>(); + remote.push(&refs, Some(PushOptions::new().remote_callbacks(callbacks)))?; + Ok(()) } |
