pub struct CommitSaver {
pub repository_url: String,
pub commit_branch_name: String,
pub commit_hash: String,
pub commit_msg: String,
pub commit_datetime: DateTime<Utc>,
}Expand description
Stores Git commit metadata for logging to Obsidian diary entries.
This struct captures all essential information about a single Git commit that will be written as a row in the daily diary table. It’s automatically populated from the current Git repository’s HEAD commit.
§Examples
use rusty_commit_saver::CommitSaver;
// Automatically populated from current Git repository
let saver = CommitSaver::new();
println!("Repository: {}", saver.repository_url);
println!("Branch: {}", saver.commit_branch_name);
println!("Hash: {}", saver.commit_hash);
println!("Message: {}", saver.commit_msg);§See Also
CommitSaver::new()- Create a new instance from current Git repoCommitSaver::append_entry_to_diary()- Write commit to diary file
Fields§
§repository_url: StringThe Git remote origin URL.
Retrieved from the repository’s origin remote. Double quotes are stripped.
§Examples
https://github.com/user/repo.gitgit@github.com:user/repo.githttps://git.sr.ht/~user/repo
commit_branch_name: StringThe current Git branch name.
Retrieved from the repository’s HEAD reference. Double quotes are stripped.
§Examples
maindevelopfeature/add-documentation
commit_hash: StringThe full SHA-1 commit hash (40 characters).
Uniquely identifies the commit in the Git repository.
§Format
Always 40 hexadecimal characters (e.g., abc123def456...)
commit_msg: StringThe formatted commit message for Obsidian display.
The message is processed for safe rendering in Markdown tables:
- Pipe characters (
|) are escaped to\| - Multiple lines are joined with
<br/> - Empty lines are filtered out
- Leading/trailing whitespace is trimmed
§Examples
Original: "feat: add feature\n\nWith details"
Formatted: "feat: add feature<br/>With details"
Original: "fix: issue | problem"
Formatted: "fix: issue \| problem"commit_datetime: DateTime<Utc>The UTC timestamp when the commit was created.
Used for:
- Generating date-based directory paths
- Displaying commit time in diary entries
- Creating frontmatter tags (week number, day of week)
§Format
Stored as DateTime<Utc> from the chrono crate.
Implementations§
Source§impl CommitSaver
impl CommitSaver
Sourcepub fn from_repo(git_repo: &Repository) -> Result<Self, Box<dyn Error>>
pub fn from_repo(git_repo: &Repository) -> Result<Self, Box<dyn Error>>
Builds a CommitSaver from an explicit repository handle.
This is the fallible core that CommitSaver::try_new, CommitSaver::new
and CommitSaver::default all delegate to. Taking the repository as a
parameter — rather than discovering the ambient working directory — keeps
the metadata extraction pure and testable, with no dependency on
process-global state such as the current directory.
§Errors
Returns an error if the repository has no resolvable HEAD, if HEAD
cannot be peeled to a commit, or if the commit timestamp is out of the
representable range.
Sourcefn try_discover(path: &Path) -> Result<Self, Box<dyn Error>>
fn try_discover(path: &Path) -> Result<Self, Box<dyn Error>>
Internal helper for path-injected repository discovery.
Discovers a Git repository at the given path and builds a CommitSaver
from its HEAD commit. This function enables testing of error cases
without mutating the process’s current directory.
§Errors
Returns an error if no Git repository can be discovered from the given
path, or if CommitSaver::from_repo fails for the discovered repo.
Sourcepub fn try_new() -> Result<Self, Box<dyn Error>>
pub fn try_new() -> Result<Self, Box<dyn Error>>
Discovers the Git repository in the current directory and builds a
CommitSaver from its HEAD commit.
This is the non-panicking counterpart to CommitSaver::new /
CommitSaver::default.
§Errors
Returns an error if no Git repository can be discovered from the current
directory, or if CommitSaver::from_repo fails for the discovered repo.
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new CommitSaver instance by discovering the current Git repository.
This function automatically:
- Discovers the Git repository in the current directory (
.) - Extracts commit metadata from the HEAD commit
- Formats the commit message for Obsidian (escapes pipes, adds
<br/>)
§Panics
Panics if:
- No Git repository is found in the current directory
- The repository has no HEAD (uninitialized repo)
- The HEAD cannot be resolved to a commit
§Examples
use rusty_commit_saver::CommitSaver;
let saver = CommitSaver::new();
println!("Commit hash: {}", saver.commit_hash);Sourcefn prepare_commit_entry_as_string(
&mut self,
path: &Path,
time_format: &str,
) -> String
fn prepare_commit_entry_as_string( &mut self, path: &Path, time_format: &str, ) -> String
Formats commit metadata as a Markdown table row for diary entry.
Generates a single table row containing all commit information in the format expected by the Obsidian diary template. The row includes pipe delimiters and ends with a newline.
§Arguments
path- The current working directory where the commit was made
§Returns
A formatted string representing one table row with these columns:
- FOLDER - Current working directory path
- TIME - Commit timestamp (HH:MM:SS format)
- COMMIT MESSAGE - Escaped and formatted commit message
- REPOSITORY URL - Git remote origin URL
- BRANCH - Current branch name
- COMMIT HASH - Full SHA-1 commit hash
§Format
| /path/to/repo | 14:30:45 | feat: add feature | https://github.com/user/repo.git | main | abc123... |§Note
This is a private helper method called by append_entry_to_diary().
The commit message has already been formatted with escaped pipes and <br/> separators
during struct initialization.
Generates Obsidian-style frontmatter tags based on the commit timestamp.
Creates three metadata tags for organizing diary entries:
- Week tag:
#datetime/week/WW(e.g.,#datetime/week/02for week 2) - Day tag:
#datetime/days/DDDD(e.g.,#datetime/days/Monday) - Category tag:
#diary/commits(constant)
These tags are used in the Obsidian diary file’s YAML frontmatter to enable:
- Filtering commits by week number
- Organizing by day of week
- Cross-referencing with other diary entries
§Returns
A vector of three strings containing formatted Obsidian tags
§Examples
use rusty_commit_saver::CommitSaver;
use chrono::{TimeZone, Utc};
let mut saver = CommitSaver {
repository_url: "https://github.com/example/repo.git".to_string(),
commit_branch_name: "main".to_string(),
commit_hash: "abc123".to_string(),
commit_msg: "feat: add feature".to_string(),
commit_datetime: Utc.with_ymd_and_hms(2025, 1, 13, 10, 30, 0).unwrap(), // Monday
};
let tags = saver.prepare_frontmatter_tags();
assert_eq!(tags.len(), 3);
assert!(tags.contains("week"));
assert!(tags.contains("Monday"));[1]
assert_eq!(tags, "#diary/commits");Sourcepub fn prepare_path_for_commit(
&mut self,
obsidian_commit_path: &Path,
template_commit_date_path: &str,
) -> String
pub fn prepare_path_for_commit( &mut self, obsidian_commit_path: &Path, template_commit_date_path: &str, ) -> String
Constructs the full file path for a diary entry based on the commit timestamp.
Combines the Obsidian commit directory path with a date-formatted subdirectory structure to create the final path where the commit entry should be saved.
§Arguments
obsidian_commit_path- Base directory path for commits (e.g.,Diaries/Commits)template_commit_date_path- Chrono format string for the date hierarchy (e.g.,%Y/%m-%B/%F.md)
§Returns
A formatted path string combining the base directory and formatted date
§Format Specifiers (Chrono)
%Y- Year (e.g.,2025)%m- Month as number (e.g.,01)%B- Full month name (e.g.,January)%F- ISO 8601 date (e.g.,2025-01-14.md)%d- Day of month (e.g.,14)
§Panics
Panics if:
- The
obsidian_commit_pathcannot be converted to a valid UTF-8 string - The path contains invalid characters that cannot be represented as a string
§Examples
use rusty_commit_saver::CommitSaver;
use std::path::PathBuf;
use chrono::{TimeZone, Utc};
let mut saver = CommitSaver {
repository_url: "https://github.com/example/repo.git".to_string(),
commit_branch_name: "main".to_string(),
commit_hash: "abc123".to_string(),
commit_msg: "feat: add feature".to_string(),
commit_datetime: Utc.with_ymd_and_hms(2025, 1, 14, 10, 30, 0).unwrap(),
};
let path = saver.prepare_path_for_commit(
&PathBuf::from("Diaries/Commits"),
"%Y/%m-%B/%F.md"
);
// Returns: "/Diaries/Commits/2025/01-January/2025-01-14.md"
assert!(path.contains("2025"));
assert!(path.contains("January"));
assert!(path.contains("2025-01-14.md"));Sourcefn prepare_date_for_commit_file(&mut self, path_format: &str) -> String
fn prepare_date_for_commit_file(&mut self, path_format: &str) -> String
Formats the commit timestamp using a Chrono date format string.
Applies the given format template to the commit’s datetime to generate a date-based directory path or filename. This enables flexible organization of diary entries by year, month, week, or custom hierarchies.
§Arguments
path_format- Chrono format string (e.g.,%Y/%m-%B/%F.md)
§Returns
A formatted date string suitable for file paths
§Common Format Specifiers
%Y- Year (4 digits, e.g.,2025)%m- Month (2 digits, e.g.,01)%B- Full month name (e.g.,January)%b- Abbreviated month (e.g.,Jan)%d- Day of month (2 digits, e.g.,14)%F- ISO 8601 date format (%Y-%m-%d, e.g.,2025-01-14)%A- Full weekday name (e.g.,Monday)%W- Week number (e.g.,02)
§Examples
// With format "%Y/%m-%B/%F.md" and datetime 2025-01-14:
// Returns: "2025/01-January/2025-01-14.md"
// With format "%Y/week-%W/%F.md" and datetime in week 2:
// Returns: "2025/week-02/2025-01-14.md"§Note
This is a private helper method called by prepare_path_for_commit().
Sourcepub fn append_entry_to_diary(
&mut self,
wiki: &PathBuf,
time_format: &str,
) -> Result<(), Box<dyn Error>>
pub fn append_entry_to_diary( &mut self, wiki: &PathBuf, time_format: &str, ) -> Result<(), Box<dyn Error>>
Appends the current commit as a table row to an Obsidian diary file.
This method writes a formatted commit entry to the specified diary file in append mode. The entry includes: current directory, timestamp, commit message, repository URL, branch, and commit hash.
§Arguments
wiki- Path to the diary file where the commit entry should be appended
§Returns
Ok(())- Successfully appended the commit entry to the fileErr(Box<dyn Error>)- If file operations fail (file doesn’t exist, permission denied, etc.)
§Errors
Returns an error if:
- The diary file cannot be opened for appending
- The current working directory cannot be determined
- File write operations fail (I/O error, permission denied)
§Examples
use rusty_commit_saver::CommitSaver;
use std::path::PathBuf;
let mut saver = CommitSaver::new();
let diary_path = PathBuf::from("/home/user/diary/2025-01-14.md");
match saver.append_entry_to_diary(&diary_path) {
Ok(()) => println!("Commit logged successfully!"),
Err(e) => eprintln!("Failed to log commit: {}", e),
}Trait Implementations§
Source§impl Clone for CommitSaver
impl Clone for CommitSaver
Source§fn clone(&self) -> CommitSaver
fn clone(&self) -> CommitSaver
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for CommitSaver
impl Debug for CommitSaver
Source§impl Default for CommitSaver
Creates a CommitSaver instance with default values from the current Git repository.
impl Default for CommitSaver
Creates a CommitSaver instance with default values from the current Git repository.
This implementation automatically discovers the Git repository in the current directory
and extracts all commit metadata from the HEAD commit. It’s the core logic used by
CommitSaver::new().
§Panics
Panics if:
- No Git repository is found in the current directory or any parent directory
- The repository has no HEAD (uninitialized or corrupted repository)
- The HEAD reference cannot be resolved to a commit
- The remote “origin” doesn’t exist
§Commit Message Processing
The commit message undergoes several transformations:
- Split into individual lines
- Trim whitespace from each line
- Escape pipe characters:
|→\|(for Markdown table compatibility) - Filter out empty lines
- Join with
<br/>separator (for Obsidian rendering)
§Examples
use rusty_commit_saver::CommitSaver;
// Using Default trait directly
let saver = CommitSaver::default();
// Equivalent to:
let saver2 = CommitSaver::new();Source§fn default() -> CommitSaver
fn default() -> CommitSaver
Builds a CommitSaver from the Git repository discovered in the current
directory.
§Panics
Panics if the current directory is not inside a Git repository, the
repository has no resolvable HEAD, or HEAD cannot be peeled to a
commit. Use CommitSaver::try_new for a non-panicking variant.