mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-28 13:26:16 +03:00
refactor & fixes for file watching
This commit is contained in:
parent
bed187a605
commit
e35580dcdd
@ -1,59 +0,0 @@
|
||||
use crate::{crdt, fs};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
pub fn deltas_path(project_path: &Path) -> Result<PathBuf, std::io::Error> {
|
||||
let path = project_path.join(".git/gb/session/deltas");
|
||||
std::fs::create_dir_all(path.clone())?;
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn get_file_deltas(
|
||||
project_path: &Path,
|
||||
file_path: &Path,
|
||||
) -> Result<Option<Vec<crdt::Delta>>, Box<dyn std::error::Error>> {
|
||||
let project_deltas_path = &deltas_path(project_path)?;
|
||||
let delta_path = project_deltas_path.join(file_path.to_path_buf());
|
||||
if delta_path.exists() {
|
||||
let raw_deltas = std::fs::read_to_string(delta_path.clone())?;
|
||||
let deltas: Vec<crdt::Delta> = serde_json::from_str(&raw_deltas)?;
|
||||
Ok(Some(deltas))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_file_deltas(
|
||||
repo: &git2::Repository,
|
||||
file_path: &Path,
|
||||
deltas: &Vec<crdt::Delta>,
|
||||
) -> Result<(), std::io::Error> {
|
||||
if deltas.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
let project_deltas_path = &deltas_path(repo.workdir().unwrap())?;
|
||||
let delta_path = project_deltas_path.join(file_path.to_path_buf());
|
||||
log::info!("Writing delta to {}", delta_path.to_str().unwrap());
|
||||
let raw_deltas = serde_json::to_string(&deltas).unwrap();
|
||||
std::fs::write(delta_path, raw_deltas)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_deltas(
|
||||
project_path: &Path,
|
||||
) -> Result<HashMap<String, Vec<crdt::Delta>>, Box<dyn std::error::Error>> {
|
||||
let deltas_path = &deltas_path(project_path)?;
|
||||
let file_paths = fs::list_files(&deltas_path)?;
|
||||
let mut deltas = HashMap::new();
|
||||
for file_path in file_paths {
|
||||
let file_path = Path::new(&file_path);
|
||||
let file_deltas = get_file_deltas(project_path, file_path)?;
|
||||
if let Some(file_deltas) = file_deltas {
|
||||
deltas.insert(file_path.to_str().unwrap().to_string(), file_deltas);
|
||||
}
|
||||
}
|
||||
Ok(deltas)
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
use crate::fs;
|
||||
use difference::{Changeset, Difference};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::SystemTime;
|
||||
use std::{collections::HashMap, path::Path, time::SystemTime};
|
||||
use yrs::{Doc, GetString, Text, Transact};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
@ -128,6 +129,119 @@ impl TextDocument {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorCause {
|
||||
IOError(std::io::Error),
|
||||
ParseJSONError(serde_json::Error),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ErrorCause {
|
||||
fn from(e: std::io::Error) -> Self {
|
||||
ErrorCause::IOError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for ErrorCause {
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
ErrorCause::ParseJSONError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
pub message: String,
|
||||
pub cause: ErrorCause,
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self.cause {
|
||||
ErrorCause::IOError(ref e) => Some(e),
|
||||
ErrorCause::ParseJSONError(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.cause {
|
||||
ErrorCause::IOError(ref e) => write!(f, "{}: {}", self.message, e),
|
||||
ErrorCause::ParseJSONError(ref e) => write!(f, "{}: {}", self.message, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_current_file_deltas(
|
||||
project_path: &Path,
|
||||
file_path: &Path,
|
||||
) -> Result<Option<Vec<Delta>>, Error> {
|
||||
let deltas_path = project_path.join(".git/gb/session/deltas");
|
||||
if !deltas_path.exists() {
|
||||
Ok(None)
|
||||
} else {
|
||||
let file_deltas_path = deltas_path.join(file_path);
|
||||
let file_deltas = std::fs::read_to_string(&file_deltas_path).map_err(|e| Error {
|
||||
message: format!(
|
||||
"Could not read delta file at {}",
|
||||
file_deltas_path.display()
|
||||
),
|
||||
cause: e.into(),
|
||||
});
|
||||
match file_deltas {
|
||||
Ok(file_deltas) => {
|
||||
let file_deltas: Vec<Delta> =
|
||||
serde_json::from_str(&file_deltas).map_err(|e| Error {
|
||||
message: format!(
|
||||
"Could not parse delta file at {}",
|
||||
file_deltas_path.display()
|
||||
),
|
||||
cause: e.into(),
|
||||
})?;
|
||||
Ok(Some(file_deltas))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_current_file_deltas(
|
||||
project_path: &Path,
|
||||
file_path: &Path,
|
||||
deltas: &Vec<Delta>,
|
||||
) -> Result<(), std::io::Error> {
|
||||
if deltas.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
let project_deltas_path = project_path.join(".git/gb/session/deltas");
|
||||
std::fs::create_dir_all(&project_deltas_path)?;
|
||||
let delta_path = project_deltas_path.join(file_path);
|
||||
log::info!("Writing deltas to {}", delta_path.to_str().unwrap());
|
||||
let raw_deltas = serde_json::to_string(&deltas)?;
|
||||
std::fs::write(delta_path, raw_deltas)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_current_deltas(project_path: &Path) -> Result<HashMap<String, Vec<Delta>>, Error> {
|
||||
let deltas_path = project_path.join(".git/gb/session/deltas");
|
||||
let file_paths = fs::list_files(&deltas_path).map_err(|e| Error {
|
||||
message: format!("Could not list delta files at {}", deltas_path.display()),
|
||||
cause: e.into(),
|
||||
})?;
|
||||
let deltas = file_paths
|
||||
.iter()
|
||||
.map_while(|file_path| {
|
||||
let file_deltas = get_current_file_deltas(project_path, Path::new(file_path));
|
||||
match file_deltas {
|
||||
Ok(Some(file_deltas)) => Some(Ok((file_path.to_owned(), file_deltas))),
|
||||
Ok(None) => None,
|
||||
Err(err) => Some(Err(err)),
|
||||
}
|
||||
})
|
||||
.collect::<Result<HashMap<String, Vec<Delta>>, Error>>()?;
|
||||
Ok(deltas)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_delta_operations_insert_end() {
|
||||
let initial_text = "hello world";
|
@ -1,11 +1,11 @@
|
||||
mod butler;
|
||||
mod crdt;
|
||||
mod deltas;
|
||||
mod fs;
|
||||
mod projects;
|
||||
mod sessions;
|
||||
mod storage;
|
||||
mod watchers;
|
||||
|
||||
use crdt::Delta;
|
||||
use deltas::Delta;
|
||||
use fs::list_files;
|
||||
use git2::Repository;
|
||||
use log;
|
||||
@ -125,7 +125,9 @@ fn list_deltas(
|
||||
project_id: &str,
|
||||
) -> Result<HashMap<String, Vec<Delta>>, Error> {
|
||||
if let Some(project) = state.projects_storage.get_project(project_id)? {
|
||||
Ok(butler::list_deltas(Path::new(project.path.as_str()))?)
|
||||
let project_path = Path::new(&project.path);
|
||||
let deltas = deltas::list_current_deltas(project_path)?;
|
||||
Ok(deltas)
|
||||
} else {
|
||||
Err("Project not found".into())
|
||||
}
|
||||
@ -189,6 +191,14 @@ pub struct Error {
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl From<deltas::Error> for Error {
|
||||
fn from(error: deltas::Error) -> Self {
|
||||
Self {
|
||||
message: error.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<projects::StorageError> for Error {
|
||||
fn from(error: projects::StorageError) -> Self {
|
||||
Self {
|
||||
|
187
src-tauri/src/sessions.rs
Normal file
187
src-tauri/src/sessions.rs
Normal file
@ -0,0 +1,187 @@
|
||||
use std::path::Path;
|
||||
|
||||
pub struct Meta {
|
||||
// timestamp of when the session was created
|
||||
pub start_ts: u64,
|
||||
// timestamp of when the session was last active
|
||||
pub last_ts: u64,
|
||||
// session branch name
|
||||
pub branch: String,
|
||||
// session commit hash
|
||||
pub commit: String,
|
||||
}
|
||||
|
||||
pub struct Session {
|
||||
pub meta: Meta,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
pub cause: ErrorCause,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match &self.cause {
|
||||
ErrorCause::IOError(err) => Some(err),
|
||||
ErrorCause::ParseIntError(err) => Some(err),
|
||||
ErrorCause::SessionExistsError => Some(self),
|
||||
ErrorCause::SessionDoesNotExistError => Some(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.cause {
|
||||
ErrorCause::IOError(ref e) => write!(f, "{}: {}", self.message, e),
|
||||
ErrorCause::ParseIntError(ref e) => write!(f, "{}: {}", self.message, e),
|
||||
ErrorCause::SessionExistsError => write!(f, "{}", self.message),
|
||||
ErrorCause::SessionDoesNotExistError => write!(f, "{}", self.message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorCause {
|
||||
IOError(std::io::Error),
|
||||
ParseIntError(std::num::ParseIntError),
|
||||
SessionExistsError,
|
||||
SessionDoesNotExistError,
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ErrorCause {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
ErrorCause::IOError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::num::ParseIntError> for ErrorCause {
|
||||
fn from(err: std::num::ParseIntError) -> Self {
|
||||
ErrorCause::ParseIntError(err)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_current_session(session_path: &Path, session: &Session) -> Result<(), Error> {
|
||||
let meta_path = session_path.join("meta");
|
||||
|
||||
std::fs::create_dir_all(meta_path.clone()).map_err(|err| Error {
|
||||
cause: err.into(),
|
||||
message: "Failed to create session directory".to_string(),
|
||||
})?;
|
||||
|
||||
let start_path = meta_path.join("start");
|
||||
std::fs::write(start_path, session.meta.start_ts.to_string()).map_err(|err| Error {
|
||||
cause: err.into(),
|
||||
message: "Failed to write session start".to_string(),
|
||||
})?;
|
||||
|
||||
let last_path = meta_path.join("last");
|
||||
std::fs::write(last_path, session.meta.last_ts.to_string()).map_err(|err| Error {
|
||||
cause: err.into(),
|
||||
message: "Failed to write session last".to_string(),
|
||||
})?;
|
||||
|
||||
let branch_path = meta_path.join("branch");
|
||||
std::fs::write(branch_path, session.meta.branch.clone()).map_err(|err| Error {
|
||||
cause: err.into(),
|
||||
message: "Failed to write session branch".to_string(),
|
||||
})?;
|
||||
|
||||
let commit_path = meta_path.join("commit");
|
||||
std::fs::write(commit_path, session.meta.commit.clone()).map_err(|err| Error {
|
||||
cause: err.into(),
|
||||
message: "Failed to write session commit".to_string(),
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_current_session(project_path: &Path, session: &Session) -> Result<(), Error> {
|
||||
let session_path = project_path.join(".git/gb/session");
|
||||
if session_path.exists() {
|
||||
write_current_session(&session_path, session)
|
||||
} else {
|
||||
Err(Error {
|
||||
cause: ErrorCause::SessionDoesNotExistError,
|
||||
message: "Session does not exist".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_current_session(project_path: &Path, session: &Session) -> Result<(), Error> {
|
||||
log::debug!("{}: Creating current session", project_path.display());
|
||||
let session_path = project_path.join(".git/gb/session");
|
||||
if session_path.exists() {
|
||||
Err(Error {
|
||||
cause: ErrorCause::SessionExistsError,
|
||||
message: "Session already exists".to_string(),
|
||||
})
|
||||
} else {
|
||||
write_current_session(&session_path, session)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_current_session(project_path: &Path) -> Result<(), std::io::Error> {
|
||||
log::debug!("{}: Deleting current session", project_path.display());
|
||||
let session_path = project_path.join(".git/gb/session");
|
||||
if session_path.exists() {
|
||||
std::fs::remove_dir_all(session_path)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_current_session(project_path: &Path) -> Result<Option<Session>, Error> {
|
||||
let session_path = project_path.join(".git/gb/session");
|
||||
if !session_path.exists() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let meta_path = session_path.join("meta");
|
||||
|
||||
let start_path = meta_path.join("start");
|
||||
let start_ts = std::fs::read_to_string(start_path)
|
||||
.map_err(|e| Error {
|
||||
cause: e.into(),
|
||||
message: "failed to read session start".to_string(),
|
||||
})?
|
||||
.parse::<u64>()
|
||||
.map_err(|e| Error {
|
||||
cause: e.into(),
|
||||
message: "failed to parse session start".to_string(),
|
||||
})?;
|
||||
|
||||
let last_path = meta_path.join("last");
|
||||
let last_ts = std::fs::read_to_string(last_path)
|
||||
.map_err(|e| Error {
|
||||
cause: e.into(),
|
||||
message: "failed to read session last".to_string(),
|
||||
})?
|
||||
.parse::<u64>()
|
||||
.map_err(|e| Error {
|
||||
cause: e.into(),
|
||||
message: "failed to parse session last".to_string(),
|
||||
})?;
|
||||
|
||||
let branch_path = meta_path.join("branch");
|
||||
let branch = std::fs::read_to_string(branch_path).map_err(|e| Error {
|
||||
cause: e.into(),
|
||||
message: "failed to read branch".to_string(),
|
||||
})?;
|
||||
|
||||
let commit_path = meta_path.join("commit");
|
||||
let commit = std::fs::read_to_string(commit_path).map_err(|e| Error {
|
||||
cause: e.into(),
|
||||
message: "failed to read commit".to_string(),
|
||||
})?;
|
||||
|
||||
Ok(Some(Session {
|
||||
meta: Meta {
|
||||
start_ts,
|
||||
last_ts,
|
||||
branch,
|
||||
commit,
|
||||
},
|
||||
}))
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
use crate::butler::{get_file_deltas, save_file_deltas};
|
||||
use crate::crdt::{Delta, TextDocument};
|
||||
use crate::deltas::{get_current_file_deltas, save_current_file_deltas, Delta, TextDocument};
|
||||
use crate::projects::Project;
|
||||
use crate::sessions;
|
||||
use git2::{Commit, Repository};
|
||||
use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::thread;
|
||||
use std::time::SystemTime;
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
use std::{fs, thread};
|
||||
use tauri::{Runtime, Window};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -77,7 +77,9 @@ pub fn watch<R: Runtime>(
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
|
||||
|
||||
watcher.watch(project_path, RecursiveMode::Recursive)?;
|
||||
|
||||
watchers
|
||||
.0
|
||||
.lock()
|
||||
@ -133,21 +135,18 @@ pub fn watch<R: Runtime>(
|
||||
fn register_file_change(
|
||||
repo: &Repository,
|
||||
kind: &EventKind,
|
||||
file_path: &Path,
|
||||
relative_file_path: &Path,
|
||||
) -> Result<Option<Vec<Delta>>, Box<dyn std::error::Error>> {
|
||||
// update meta files every time file change is detected
|
||||
write_beginning_meta_files(&repo)?;
|
||||
|
||||
if !file_path.is_file() {
|
||||
// only handle file changes
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if repo.is_path_ignored(&file_path).unwrap_or(true) {
|
||||
if repo.is_path_ignored(&relative_file_path).unwrap_or(true) {
|
||||
// make sure we're not watching ignored files
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let file_path = repo.workdir().unwrap().join(relative_file_path);
|
||||
|
||||
// update meta files every time file change is detected
|
||||
write_beginning_meta_files(&repo)?;
|
||||
|
||||
if EventKind::is_modify(&kind) {
|
||||
log::info!("File modified: {:?}", file_path);
|
||||
} else if EventKind::is_create(&kind) {
|
||||
@ -159,7 +158,7 @@ fn register_file_change(
|
||||
// first, we need to check if the file exists in the meta commit
|
||||
let meta_commit = get_meta_commit(&repo);
|
||||
let tree = meta_commit.tree().unwrap();
|
||||
let commit_blob = if let Ok(object) = tree.get_path(file_path) {
|
||||
let commit_blob = if let Ok(object) = tree.get_path(relative_file_path) {
|
||||
// if file found, check if delta file exists
|
||||
let blob = object.to_object(&repo).unwrap().into_blob().unwrap();
|
||||
let contents = String::from_utf8(blob.content().to_vec()).unwrap();
|
||||
@ -169,7 +168,7 @@ fn register_file_change(
|
||||
};
|
||||
|
||||
// second, get non-flushed file deltas
|
||||
let deltas = get_file_deltas(&repo.workdir().unwrap(), file_path)?;
|
||||
let deltas = get_current_file_deltas(&repo.workdir().unwrap(), relative_file_path)?;
|
||||
|
||||
// depending on the above, we can create TextDocument
|
||||
let mut text_doc = match (commit_blob, deltas) {
|
||||
@ -188,7 +187,7 @@ fn register_file_change(
|
||||
|
||||
// if the file was modified, save the deltas
|
||||
let deltas = text_doc.get_deltas();
|
||||
save_file_deltas(repo, file_path, &deltas)?;
|
||||
save_current_file_deltas(repo.workdir().unwrap(), relative_file_path, &deltas)?;
|
||||
return Ok(Some(deltas));
|
||||
}
|
||||
|
||||
@ -204,50 +203,36 @@ pub fn get_meta_commit(repo: &Repository) -> Commit {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_now_to(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
fs::write(
|
||||
path,
|
||||
SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
.to_string()
|
||||
.as_bytes(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// this function is called when the user modifies a file, it writes starting metadata if not there
|
||||
// and also touches the last activity timestamp, so we can tell when we are idle
|
||||
fn write_beginning_meta_files(repo: &Repository) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let meta_path = repo.path().join(Path::new("gb/session/meta"));
|
||||
// create the parent directory recurisvely if it doesn't exist
|
||||
std::fs::create_dir_all(meta_path.clone())?;
|
||||
|
||||
// check if the file .git/gb/meta/start exists and if not, write the current timestamp into it
|
||||
let meta_session_start = meta_path.join(Path::new("session-start"));
|
||||
if !meta_session_start.exists() {
|
||||
write_now_to(&meta_session_start)?;
|
||||
let project_path = repo.workdir().unwrap();
|
||||
let now_ts = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
match sessions::get_current_session(project_path)
|
||||
.map_err(|e| format!("Error while getting current session: {}", e.to_string()))?
|
||||
{
|
||||
Some(mut session) => {
|
||||
session.meta.last_ts = now_ts;
|
||||
sessions::update_current_session(project_path, &session)
|
||||
.map_err(|e| format!("Error while updating current session: {}", e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
let head = repo.head()?;
|
||||
let session = sessions::Session {
|
||||
meta: sessions::Meta {
|
||||
start_ts: now_ts,
|
||||
last_ts: now_ts,
|
||||
branch: head.name().unwrap().to_string(),
|
||||
commit: head.peel_to_commit()?.id().to_string(),
|
||||
},
|
||||
};
|
||||
sessions::create_current_session(project_path, &session)
|
||||
.map_err(|e| format!("Error while creating current session: {}", e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// check if the file .git/gb/session/meta/branch exists and if not, write the current branch name into it
|
||||
let meta_branch = meta_path.join(Path::new("branch"));
|
||||
if !meta_branch.exists() {
|
||||
let branch = repo.head()?;
|
||||
let branch_name = branch.name().unwrap();
|
||||
fs::write(meta_branch, branch_name)?;
|
||||
}
|
||||
|
||||
// check if the file .git/gb/session/meta/commit exists and if not, write the current commit hash into it
|
||||
let meta_commit = meta_path.join(Path::new("commit"));
|
||||
if !meta_commit.exists() {
|
||||
let commit = repo.head().unwrap().peel_to_commit()?;
|
||||
fs::write(meta_commit, commit.id().to_string())?;
|
||||
}
|
||||
|
||||
// ALWAYS write the last time we did this
|
||||
let meta_session_last = meta_path.join(Path::new("session-last"));
|
||||
write_now_to(&meta_session_last)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{fs, projects::Project};
|
||||
use crate::{fs, projects::Project, sessions};
|
||||
use filetime::FileTime;
|
||||
use git2::{IndexTime, Repository};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{
|
||||
fs::{read_to_string, File},
|
||||
fs::File,
|
||||
io::{BufReader, Read},
|
||||
os::unix::prelude::MetadataExt,
|
||||
path::Path,
|
||||
@ -77,7 +77,7 @@ fn check_for_changes(repo: &Repository) -> Result<(), Box<dyn std::error::Error>
|
||||
commit_oid
|
||||
);
|
||||
|
||||
clean_up_session(repo)?;
|
||||
sessions::delete_current_session(repo.workdir().unwrap())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -85,42 +85,34 @@ fn check_for_changes(repo: &Repository) -> Result<(), Box<dyn std::error::Error>
|
||||
// TODO: if we see it is not a FF, pull down the remote, determine order, rewrite the commit line, and push again
|
||||
}
|
||||
|
||||
fn read_timestamp(path: &Path) -> Result<u64, Box<dyn std::error::Error>> {
|
||||
let raw = read_to_string(path)?;
|
||||
let ts = raw.trim().parse::<u64>()?;
|
||||
Ok(ts)
|
||||
}
|
||||
|
||||
// make sure that the .git/gb/session directory exists (a session is in progress)
|
||||
// and that there has been no activity in the last 5 minutes (the session appears to be over)
|
||||
// and the start was at most an hour ago
|
||||
fn ready_to_commit(repo: &Repository) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
let repo_path = repo.path();
|
||||
let last_file = repo_path.join("gb/session/meta/session-last");
|
||||
let start_file = repo_path.join("gb/session/meta/session-start");
|
||||
if !last_file.exists() {
|
||||
log::debug!("{}: no current session", repo_path.display());
|
||||
return Ok(false);
|
||||
};
|
||||
if let Some(current_session) = sessions::get_current_session(repo.workdir().unwrap())? {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as u64;
|
||||
|
||||
let session_start_ts = read_timestamp(&start_file)?;
|
||||
let session_last_ts = read_timestamp(&last_file)?;
|
||||
let now = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as u64;
|
||||
let elapsed_last = now - current_session.meta.last_ts;
|
||||
let elapsed_start = now - current_session.meta.start_ts;
|
||||
|
||||
let elapsed_last = now - session_last_ts;
|
||||
let elapsed_start = now - session_start_ts;
|
||||
|
||||
if (elapsed_last > FIVE_MINUTES) || (elapsed_start > ONE_HOUR) {
|
||||
Ok(true)
|
||||
if (elapsed_last > FIVE_MINUTES) || (elapsed_start > ONE_HOUR) {
|
||||
Ok(true)
|
||||
} else {
|
||||
log::debug!(
|
||||
"Not ready to commit {} yet. ({} seconds elapsed, {} seconds since start)",
|
||||
repo.workdir().unwrap().display(),
|
||||
elapsed_last,
|
||||
elapsed_start
|
||||
);
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
log::debug!(
|
||||
"Not ready to commit {} yet. ({} seconds elapsed, {} seconds since start)",
|
||||
repo.workdir().unwrap().display(),
|
||||
elapsed_last,
|
||||
elapsed_start
|
||||
"No current session for {}",
|
||||
repo.workdir().unwrap().display()
|
||||
);
|
||||
Ok(false)
|
||||
}
|
||||
@ -395,11 +387,3 @@ fn write_gb_commit(gb_tree: git2::Oid, repo: &Repository) -> Result<git2::Oid, g
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this stops the current session by deleting the .git/gb/session directory
|
||||
fn clean_up_session(repo: &git2::Repository) -> Result<(), std::io::Error> {
|
||||
// delete the .git/gb/session directory
|
||||
let session_path = repo.path().join("gb/session");
|
||||
std::fs::remove_dir_all(session_path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user