summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-04-29 08:16:10 +0300
committerLars Wirzenius <liw@liw.fi>2021-04-29 09:07:04 +0300
commiteed62b29f55e02ea84a4b470651d5c7247633d20 (patch)
tree85c33bdcad0e7f65cd2fedc8a8677368ed564186
parentfdf77e57306f83672253f15e78ceaaadebfb0dec (diff)
downloadjt2-eed62b29f55e02ea84a4b470651d5c7247633d20.tar.gz
feat: make a journal a git repository and commit new entries
-rw-r--r--jt.md4
-rw-r--r--src/error.rs20
-rw-r--r--src/git.rs62
-rw-r--r--src/journal.rs22
-rw-r--r--src/lib.rs1
-rw-r--r--subplot/jt.py13
-rw-r--r--subplot/jt.yaml3
7 files changed, 125 insertions, 0 deletions
diff --git a/jt.md b/jt.md
index 958de77..a06ccf4 100644
--- a/jt.md
+++ b/jt.md
@@ -130,6 +130,7 @@ given an installed jt
when I run jt2 --dirname jrnl init default "My test journal"
then command is successful
and directory jrnl exists
+then there are no uncommitted changes in jrnl
when I run jt2 --dirname jrnl is-journal
then command is successful
@@ -169,6 +170,7 @@ and file name <FILE> ends with .mdwn
and journal entry <FILE> contains "Abracadabra"
and journal entry <FILE> contains "Open sesame!"
and there are no drafts in jrnl
+and there are no uncommitted changes in jrnl
~~~
~~~{#append.sh .file .numberLines}
@@ -219,6 +221,7 @@ then there are two journal entries in jrnl, at FILE1 and FILE2
then journal entry <FILE1> contains "Abracadabra"
then journal entry <FILE2> contains "Simsalabim"
then there are no drafts in jrnl
+then there are no uncommitted changes in jrnl
~~~
@@ -265,6 +268,7 @@ then stderr contains "foo"
when I run jt2 --editor=none --dirname=jrnl new-topic foo "Things about Foo"
then command is successful
then file jrnl/foo.mdwn contains "Things about Foo"
+then there are no uncommitted changes in jrnl
when I run jt2 --editor=none --dirname=jrnl new --topic foo "Abracadabra"
then command is successful
diff --git a/src/error.rs b/src/error.rs
index 89a10c1..8db0991 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -62,6 +62,22 @@ pub enum JournalError {
#[error("failed to stat draft in {0}: {1}")]
StatDraft(PathBuf, #[source] std::io::Error),
+ /// Error spawning git.
+ #[error("failed to start git in {0}: {1}")]
+ SpawnGit(PathBuf, std::io::Error),
+
+ /// Git init failed.
+ #[error("git init failed in {0}: {1}")]
+ GitInit(PathBuf, String),
+
+ /// Git add failed.
+ #[error("git add failed in {0}: {1}")]
+ GitAdd(PathBuf, String),
+
+ /// Git commit failed.
+ #[error("git commit failed in {0}: {1}")]
+ GitCommit(PathBuf, String),
+
/// Error spawning editor.
#[error("failed to start editor {0}: {1}")]
SpawnEditor(PathBuf, #[source] std::io::Error),
@@ -77,4 +93,8 @@ pub enum JournalError {
/// Failed to render a Tera template.
#[error("template {0} failed to render: {1}")]
TemplateRender(String, #[source] tera::Error),
+
+ /// Failed to make a path relative to a directory.
+ #[error("failed to make {0} relative to {1}: {2}")]
+ RelativePath(PathBuf, PathBuf, std::path::StripPrefixError),
}
diff --git a/src/git.rs b/src/git.rs
new file mode 100644
index 0000000..5f1f66c
--- /dev/null
+++ b/src/git.rs
@@ -0,0 +1,62 @@
+use crate::error::JournalError;
+use log::debug;
+use std::ffi::OsString;
+use std::path::Path;
+use std::process::Command;
+
+pub fn init<P: AsRef<Path>>(dirname: P) -> Result<(), JournalError> {
+ let dirname = dirname.as_ref();
+ debug!("git init {}", dirname.display());
+
+ let output = Command::new("git")
+ .arg("init")
+ .current_dir(dirname)
+ .output()
+ .map_err(|err| JournalError::SpawnGit(dirname.to_path_buf(), err))?;
+ debug!("git init exit code was {:?}", output.status.code());
+ if !output.status.success() {
+ let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
+ return Err(JournalError::GitInit(dirname.to_path_buf(), stderr));
+ }
+ Ok(())
+}
+
+pub fn add<P: AsRef<Path>>(dirname: P, files: &[P]) -> Result<(), JournalError> {
+ let args = &["add", "--"];
+ let mut args: Vec<OsString> = args.iter().map(|x| OsString::from(x)).collect();
+ for f in files {
+ args.push(OsString::from(f.as_ref()));
+ }
+
+ let dirname = dirname.as_ref();
+ debug!("git add in {}", dirname.display());
+
+ let output = Command::new("git")
+ .args(&args)
+ .current_dir(dirname)
+ .output()
+ .map_err(|err| JournalError::SpawnGit(dirname.to_path_buf(), err))?;
+ debug!("git exit code was {:?}", output.status.code());
+ if !output.status.success() {
+ let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
+ return Err(JournalError::GitAdd(dirname.to_path_buf(), stderr));
+ }
+ Ok(())
+}
+
+pub fn commit<P: AsRef<Path>>(dirname: P, msg: &str) -> Result<(), JournalError> {
+ let dirname = dirname.as_ref();
+ debug!("git commit in {}", dirname.display());
+
+ let output = Command::new("git")
+ .args(&["commit", "-m", msg])
+ .current_dir(dirname)
+ .output()
+ .map_err(|err| JournalError::SpawnGit(dirname.to_path_buf(), err))?;
+ debug!("git exit code was {:?}", output.status.code());
+ if !output.status.success() {
+ let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
+ return Err(JournalError::GitCommit(dirname.to_path_buf(), stderr));
+ }
+ Ok(())
+}
diff --git a/src/journal.rs b/src/journal.rs
index 41e6d3d..2610a44 100644
--- a/src/journal.rs
+++ b/src/journal.rs
@@ -1,4 +1,5 @@
use crate::error::JournalError;
+use crate::git;
use crate::template::Templates;
use chrono::{DateTime, Local};
use std::path::{Path, PathBuf};
@@ -21,6 +22,7 @@ impl Journal {
pub fn init(path: &Path, entries: &Path) -> Result<Self, JournalError> {
std::fs::create_dir(path)
.map_err(|err| JournalError::CreateDirectory(path.to_path_buf(), err))?;
+ git::init(path)?;
std::fs::create_dir(entries)
.map_err(|err| JournalError::CreateDirectory(entries.to_path_buf(), err))?;
Ok(Self {
@@ -49,6 +51,13 @@ impl Journal {
&self.dirname
}
+ fn relative(&self, path: &Path) -> Result<PathBuf, JournalError> {
+ let path = path.strip_prefix(self.dirname()).map_err(|err| {
+ JournalError::RelativePath(path.to_path_buf(), self.dirname().to_path_buf(), err)
+ })?;
+ Ok(path.to_path_buf())
+ }
+
fn drafts(&self) -> PathBuf {
self.dirname().join("drafts")
}
@@ -153,6 +162,12 @@ impl Journal {
std::fs::rename(filename, &entry).map_err(|err| {
JournalError::RenameEntry(filename.to_path_buf(), entry.to_path_buf(), err)
})?;
+
+ let entry = self.relative(&entry)?;
+ git::add(self.dirname(), &[&entry])?;
+
+ let msg = format!("journal entry {}", entry.display());
+ git::commit(self.dirname(), &msg)?;
Ok(())
}
@@ -165,6 +180,13 @@ impl Journal {
std::fs::write(&pathname, text)
.map_err(|err| JournalError::WriteTopic(pathname.to_path_buf(), err))?;
self.edit(editor, &pathname)?;
+
+ let topic = self.relative(&pathname)?;
+ git::add(self.dirname(), &[&topic])?;
+
+ let msg = format!("new topic {}", topic.display());
+ git::commit(self.dirname(), &msg)?;
+
Ok(())
}
}
diff --git a/src/lib.rs b/src/lib.rs
index a634111..509e6f1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,7 @@
pub mod cmd;
pub mod config;
pub mod error;
+pub mod git;
pub mod journal;
pub mod opt;
pub mod template;
diff --git a/subplot/jt.py b/subplot/jt.py
index d6023b3..0117736 100644
--- a/subplot/jt.py
+++ b/subplot/jt.py
@@ -166,3 +166,16 @@ def file_name_has_suffix(ctx, varname=None, suffix=None):
variables = ctx.get("variables", {})
filename = variables[varname]
assert filename.endswith(suffix)
+
+
+def git_is_clean(ctx, dirname=None):
+ runcmd_run = globals()["runcmd_run"]
+ runcmd_exit_code_is_zero = globals()["runcmd_exit_code_is_zero"]
+ runcmd_get_stdout = globals()["runcmd_get_stdout"]
+ assert_eq = globals()["assert_eq"]
+
+ runcmd_run(ctx, ["git", "status", "--porcelain"], cwd=dirname)
+ runcmd_exit_code_is_zero(ctx)
+
+ stdout = runcmd_get_stdout(ctx)
+ assert_eq(stdout, "")
diff --git a/subplot/jt.yaml b/subplot/jt.yaml
index a3d4f0f..dc2a666 100644
--- a/subplot/jt.yaml
+++ b/subplot/jt.yaml
@@ -60,3 +60,6 @@
- then: file name <{varname}> ends with {suffix}
function: file_name_has_suffix
+
+- then: there are no uncommitted changes in {dirname}
+ function: git_is_clean