diff --git a/README.md b/README.md index 930cf48..65c0a15 100644 --- a/README.md +++ b/README.md @@ -1,253 +1,14 @@ -# 🎨 GlyphIt +

Glyphit

-> **Emoji-powered Git commits made simple** +## ❓ What is Glyphit? -GlyphIt is a blazingly fast, Rust-based CLI tool that brings the power of conventional commits with emojis to your Git workflow. Say goodbye to boring commit messages and hello to expressive, standardized commits that communicate intent at a glance. +Is an open source project which aims to standardize the usage of emojis in git commit. -## ✨ Features +Following the footsteps of [Gitmoji](https://gitmoji.dev/), it aims to provide a fast way to structure your commit, according to a conventional standard. -- **🎯 Interactive Emoji Selection** - Choose from a curated list of conventional commit emojis -- **⚡ Lightning Fast** - Built with Rust for maximum performance -- **🔒 Type-Safe** - Leverages Rust's type system for reliability -- **📦 Zero Dependencies** - Self-contained binaries for all major platforms -- **🎨 Gitmoji Compatible** - Follows the popular gitmoji convention -- **🔄 Full Git Integration** - Seamlessly works with your existing Git workflow +## ❓ How to use Glyphit? +For every question about the projects, from the architecture to the download, consult our [documentation](https://nickghignatti.github.io/glyphit/). -## 🚀 Quick Start +## ™️ License -```bash -# Add files to staging -glyphit add file1.txt file2.txt - -# Create an emoji-powered commit -glyphit commit - -# Push to remote -glyphit push -``` - -## 📥 Installation - -### Download Pre-built Binaries - -Download the latest release for your platform from the [Releases page](https://github.com/yourusername/glyphit/releases): - -#### Linux (x86_64) -```bash -# Download and extract -curl -LO https://github.com/yourusername/glyphit/releases/latest/download/glyphit-Linux-musl-x86_64.tar.gz -tar -xzf glyphit-Linux-musl-x86_64.tar.gz - -# Move to PATH -sudo mv glyphit /usr/local/bin/ -``` - -#### Linux (ARM64) -```bash -# Download and extract -curl -LO https://github.com/yourusername/glyphit/releases/latest/download/glyphit-Linux-musl-arm64.tar.gz -tar -xzf glyphit-Linux-musl-arm64.tar.gz - -# Move to PATH -sudo mv glyphit /usr/local/bin/ -``` - -#### macOS (x86_64) -```bash -# Download and extract -curl -LO https://github.com/yourusername/glyphit/releases/latest/download/glyphit-macOS-x86_64.tar.gz -tar -xzf glyphit-macOS-x86_64.tar.gz - -# Move to PATH -sudo mv glyphit /usr/local/bin/ -``` - -#### Windows (x86_64) -Download `glyphit-Windows-msvc-x86_64.zip` from the releases page, extract it, and add the directory to your PATH. - -### Build from Source - -```bash -# Clone the repository -git clone https://github.com/yourusername/glyphit.git -cd glyphit - -# Build with cargo -cargo build --release - -# The binary will be in target/release/glyphit -``` - -## 📖 Usage - -### Adding Files - -Add files to the staging area just like `git add`: - -```bash -glyphit add src/main.rs -glyphit add file1.txt file2.txt file3.txt -``` - -### Creating Commits - -Launch the interactive commit creator: - -```bash -glyphit commit -``` - -You'll be prompted to: -1. **Select an emoji** from the conventional commit types -2. **Enter a commit message** describing your changes -3. **Add breaking changes** (optional) - -**Available Emoji Types:** - -| Emoji | Code | Description | -|-------|------|-------------| -| 🎨 | `:art:` | Improve structure/format of the code | -| ⚡️ | `:zap:` | Improve performance | -| 🔥 | `:fire:` | Remove code or files | -| 🐛 | `:bug:` | Fix a bug | -| ✨ | `:sparkles:` | Introduce new features | - -### Pushing Changes - -Push your commits to the remote repository: - -```bash -glyphit push -``` - -Supports both HTTPS and SSH authentication methods. - -## 🎯 Complete Workflow Example - -```bash -# Make your changes -echo "new feature" > feature.txt - -# Stage the changes -glyphit add feature.txt - -# Create an emoji commit -glyphit commit -# Select: ✨ :sparkles: Introduce new features -# Message: add new user authentication feature -# Breaking: (leave empty) - -# Push to remote -glyphit push -``` - -## 🏗️ Architecture - -GlyphIt is built with modern software engineering principles: - -- **Domain-Driven Design** - Clear separation of concerns with domain types -- **Test-Driven Development** - Comprehensive test coverage for reliability -- **Functional Core** - Pure functions with minimal side effects -- **Type Safety** - Leverages Rust's strong type system - -### Project Structure - -``` -glyphit/ -├── src/ -│ ├── main.rs # Entry point -│ ├── functions/ # Core Git operations -│ │ ├── add.rs # File staging -│ │ ├── commit.rs # Commit creation -│ │ └── push.rs # Remote pushing -│ └── types/ # Domain types -│ ├── commands.rs # CLI command definitions -│ └── repository.rs # Repository utilities -├── docs/ # Documentation -└── .github/workflows/ # CI/CD pipelines -``` - -## 🔧 Configuration - -GlyphIt uses your existing Git configuration: - -```bash -# Set your Git identity (if not already set) -git config --global user.name "Your Name" -git config --global user.email "your.email@example.com" -``` - -## 🤝 Contributing - -Contributions are welcome! Here's how you can help: - -1. **Fork the repository** -2. **Create a feature branch** (`git checkout -b feature/amazing-feature`) -3. **Make your changes** -4. **Run tests** (`cargo test`) -5. **Commit with GlyphIt** (`glyphit commit`) -6. **Push to your fork** (`glyphit push`) -7. **Open a Pull Request** - -### Development Setup - -```bash -# Clone your fork -git clone https://github.com/yourusername/glyphit.git -cd glyphit - -# Install dependencies and build -cargo build - -# Run tests -cargo test - -# Run with debug output -cargo run -- add file.txt -cargo run -- commit -``` - -## 📋 Requirements - -- **Rust Edition**: 2024 -- **Git**: 2.0 or higher -- **Platforms**: Linux (x86_64, ARM64), macOS (x86_64), Windows (x86_64) - -## 🐛 Troubleshooting - -### Authentication Issues - -**HTTPS Authentication:** -```bash -# Configure credential helper -git config --global credential.helper store -``` - -**SSH Authentication:** -```bash -# Ensure SSH agent is running with your key -ssh-add ~/.ssh/id_rsa -``` - -### Repository Not Found - -Make sure you're in a Git repository: -```bash -git status -``` - -## 📄 License - -This project is licensed under the GLPv3 License - see the [LICENSE](LICENSE) file for details. - -## 🙏 Acknowledgments - -- Inspired by [gitmoji](https://gitmoji.dev/) - An emoji guide for commit messages -- Built with [git2-rs](https://github.com/rust-lang/git2-rs) - Rust bindings to libgit2 -- Powered by [clap](https://github.com/clap-rs/clap) - Command line argument parser - -## 🌟 Star History - -If you find GlyphIt useful, please consider giving it a star! ⭐ - ---- \ No newline at end of file +This code is available under the [GPLv3](LICENSE) license. diff --git a/gly.zip b/gly.zip deleted file mode 100644 index b3beab3..0000000 Binary files a/gly.zip and /dev/null differ diff --git a/src/functions/add.rs b/src/functions/add.rs index e2df0c2..b92b469 100644 --- a/src/functions/add.rs +++ b/src/functions/add.rs @@ -1,5 +1,5 @@ -use git2::{Error, Repository}; use crate::types::repository::get_current_repository; +use git2::{Error, Repository}; /// Adds a list of files to the staging area (index) of a Git repository. /// @@ -11,7 +11,7 @@ use crate::types::repository::get_current_repository; /// /// * `files` - A reference to a vector of strings representing file paths to add. /// * `repo` - An optional reference to a `Repository`. If `None`, the function tries -/// to find the current repository automatically. +/// to find the current repository automatically. /// /// # Errors /// @@ -26,32 +26,29 @@ use crate::types::repository::get_current_repository; /// * `Ok(0)` on success. pub fn add(files: &Vec, repo: Option<&Repository>) -> Result<(), Error> { let owned_repo; - let current_repo = match repo{ + let current_repo = match repo { Some(r) => r, _ => { - owned_repo = get_current_repository().map_err(|e| e)?; + owned_repo = get_current_repository()?; &owned_repo } }; // get the index (staging area) - let mut index = match current_repo.index() { - Ok(index) => index, - Err(e) => return Err(e), - }; + let mut index = current_repo.index()?; let corrected_files: Vec = if files[0] == "." { - vec![files[0].to_string()] + vec![files[0].to_string()] } else { - files.iter() - .map(|f| { - f.strip_prefix(".\\").unwrap_or(f) - .to_string() - }) + files + .iter() + .map(|f| f.strip_prefix(".\\").unwrap_or(f).to_string()) .collect() }; - index.add_all(corrected_files, git2::IndexAddOption::DEFAULT, None).expect("Error while adding all files"); + index + .add_all(corrected_files, git2::IndexAddOption::DEFAULT, None) + .expect("Error while adding all files"); // write index to disk index.write() @@ -61,9 +58,9 @@ pub fn add(files: &Vec, repo: Option<&Repository>) -> Result<(), Error> mod tests { use super::*; use git2::Repository; - use tempfile::tempdir; use std::fs::File; use std::path::Path; + use tempfile::tempdir; #[test] fn test_add() { @@ -83,4 +80,4 @@ mod tests { let index = repo.index().unwrap(); assert!(index.get_path(Path::new("dummy.txt"), 0).is_some()); } -} \ No newline at end of file +} diff --git a/src/functions/commit.rs b/src/functions/commit.rs index 69c63ac..bc2a47e 100644 --- a/src/functions/commit.rs +++ b/src/functions/commit.rs @@ -1,7 +1,7 @@ -use std::io::Write; +use crate::types::repository::get_current_repository; use git2::{Error, ErrorClass, ErrorCode, Oid, Repository, Signature}; use inquire::{InquireError, Select}; -use crate::types::repository::get_current_repository; +use std::io::Write; fn user_input(message: String) -> String { use std::io; @@ -11,8 +11,8 @@ fn user_input(message: String) -> String { let mut input = String::new(); match io::stdin().read_line(&mut input) { - Ok(_goes_into_input_above) => {}, - Err(_no_updates_is_fine) => {}, + Ok(_goes_into_input_above) => {} + Err(_no_updates_is_fine) => {} } input.trim().to_string() } @@ -105,7 +105,7 @@ fn select_emoji() -> Result { /// # Arguments /// /// * `repo` - An optional reference to a `Repository`. If `None`, the function -/// attempts to find the current repository automatically. +/// attempts to find the current repository automatically. /// /// # Errors /// @@ -119,7 +119,7 @@ fn select_emoji() -> Result { /// /// # Returns /// -/// * `Ok(0)` on successful commit. +/// * `Ok(0)` on a successful commit. /// /// # Workflow /// @@ -132,17 +132,14 @@ fn select_emoji() -> Result { /// - Creates a commit with the assembled information. pub fn commit(repo: Option<&Repository>, debug: bool) -> Result { let owned_repo; - let current_repo = match repo{ + let current_repo = match repo { Some(r) => r, _ => { owned_repo = get_current_repository().map_err(|e| e)?; &owned_repo } }; - let repo_configuration = match current_repo.config() { - Ok(config) => config, - Err(e) => return Err(e) - }; + let repo_configuration = current_repo.config()?; let mut commit_message = String::new(); @@ -156,17 +153,23 @@ pub fn commit(repo: Option<&Repository>, debug: bool) -> Result { } _ => "", }, - Err(e) => return Err(Error::new(ErrorCode::NotFound, ErrorClass::Invalid, e.to_string())) + Err(e) => { + return Err(Error::new( + ErrorCode::NotFound, + ErrorClass::Invalid, + e.to_string(), + )); + } }; commit_message.push_str(emoji); let title = user_input("Provide a commit title > ".to_string()); commit_message.push_str(title.as_str()); - commit_message.push_str("\n"); + commit_message.push('\n'); let message = user_input("Provide a commit message > ".to_string()); commit_message.push_str(message.as_str()); - commit_message.push_str("\n"); + commit_message.push('\n'); let breaking_changes = user_input("Provide a breaking changes description > ".to_string()); commit_message.push_str(format!("BREAKING CHANGES: {}\n", breaking_changes).as_str()); } else { @@ -177,27 +180,16 @@ pub fn commit(repo: Option<&Repository>, debug: bool) -> Result { let email = repo_configuration.get_string("user.email")?.to_string(); // get index and write tree - let mut index = match current_repo.index() { - Ok(idx) => idx, - Err(e) => return Err(e) - }; - let tree_oid = match index.write_tree() { - Ok(oid) => oid, - Err(e) => return Err(e) - }; - let tree = match current_repo.find_tree(tree_oid) { - Ok(idx_tree) => idx_tree, - Err(e) => return Err(e) - }; + let mut index = current_repo.index()?; + let tree_oid = index.write_tree()?; + let tree = current_repo.find_tree(tree_oid)?; // get HEAD commit to set as parent let parents = match current_repo.head() { - Ok(head) => { - match head.peel_to_commit() { - Ok(commit) => vec![commit], - Err(e) => return Err(e), - } - } + Ok(head) => match head.peel_to_commit() { + Ok(commit) => vec![commit], + Err(e) => return Err(e), + }, Err(_) => vec![], // Unborn branch, so NO parent }; @@ -208,7 +200,7 @@ pub fn commit(repo: Option<&Repository>, debug: bool) -> Result { Some("HEAD"), &signature, &signature, - &(commit_message.as_str()), + commit_message.as_str(), &tree, &parent_refs, ) @@ -217,11 +209,10 @@ pub fn commit(repo: Option<&Repository>, debug: bool) -> Result { #[cfg(test)] mod tests { use super::*; + use crate::functions::add::add; use git2::Repository; use std::fs::File; use tempfile::tempdir; - use crate::functions::add::add; - #[test] fn test_commit() { @@ -242,7 +233,15 @@ mod tests { let mut index = repo.index().unwrap(); let tree_id = index.write_tree().unwrap(); tree = repo.find_tree(tree_id).unwrap(); - repo.commit(Some("HEAD"), &signature, &signature, "Initial commit", &tree, &[]).unwrap(); + repo.commit( + Some("HEAD"), + &signature, + &signature, + "Initial commit", + &tree, + &[], + ) + .unwrap(); let result = commit(Some(&repo), true); @@ -252,4 +251,4 @@ mod tests { let commit = head.peel_to_commit().unwrap(); assert_eq!(commit.message().unwrap(), "unit testing"); } -} \ No newline at end of file +} diff --git a/src/functions/mod.rs b/src/functions/mod.rs index 4d2f78b..29a4e3a 100644 --- a/src/functions/mod.rs +++ b/src/functions/mod.rs @@ -1,3 +1,3 @@ pub mod add; pub mod commit; -pub mod push; \ No newline at end of file +pub mod push; diff --git a/src/functions/push.rs b/src/functions/push.rs index afea4a4..b32cebf 100644 --- a/src/functions/push.rs +++ b/src/functions/push.rs @@ -31,7 +31,7 @@ fn create_ssh_callbacks() -> RemoteCallbacks<'static> { /// # Arguments /// /// * `repo` - An optional reference to a `Repository`. If `None`, -/// the function tries to find the current repository automatically. +/// the function tries to find the current repository automatically. /// /// # Errors /// @@ -54,37 +54,27 @@ pub fn push(repo: Option<&Repository>) -> Result<(), Error> { } }; - let head = match current_repo.head() { - Ok(h) => h, - Err(e) => return Err(e), - }; + let head = current_repo.head()?; let branch = head.shorthand().unwrap(); let refspec = format!("refs/heads/{}:refs/heads/{}", branch, branch); let mut push_options = PushOptions::new(); - let repo_configuration = match current_repo.config() { - Ok(config) => config, - Err(e) => return Err(e) - }; + let repo_configuration = current_repo.config()?; - let url = repo_configuration.get_string("remote.origin.url")?.to_string(); + let url = repo_configuration + .get_string("remote.origin.url")? + .to_string(); if !url.contains("https") { let callbacks = create_ssh_callbacks(); push_options.remote_callbacks(callbacks); } else { - let config = match current_repo.config() { - Ok(config) => config, - Err(e) => return Err(e) - }; + let config = current_repo.config()?; let callbacks = create_https_callback(config); push_options.remote_callbacks(callbacks); } - let mut origin = match current_repo.find_remote("origin") { - Ok(org) => org, - Err(e) => return Err(e), - }; + let mut origin = current_repo.find_remote("origin")?; origin.push(&[refspec], Some(&mut push_options)) } diff --git a/src/main.rs b/src/main.rs index 9207894..1c31b1a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ -mod types; mod functions; +mod types; use clap::Parser; -use types::commands::{Cli, Command}; use functions::add::add; use functions::commit::commit; use functions::push::push; +use types::commands::{Cli, Command}; fn main() { let cli = Cli::parse(); diff --git a/src/types/commands.rs b/src/types/commands.rs index 98d89d5..8b55b03 100644 --- a/src/types/commands.rs +++ b/src/types/commands.rs @@ -8,7 +8,7 @@ use clap::{Parser, Subcommand}; /// # Possible values /// /// * `Add` - Adds one or more files to the staging area. -/// Contains a single field: +/// Contains a single field: /// - `files` - A vector of file paths (`Vec`) to add. /// /// * `Commit` - Creates a new commit with a message enriched with emoji. @@ -17,11 +17,9 @@ use clap::{Parser, Subcommand}; /// #[derive(Subcommand, Debug)] pub(crate) enum Command { - Add { - files: Vec - }, + Add { files: Vec }, Commit, - Push + Push, } /// Command-line interface (CLI) argument parser for the `glyphit` tool. @@ -32,7 +30,7 @@ pub(crate) enum Command { /// # Fields /// /// * `command` - The subcommand specified by the user. Determines the -/// operation the CLI will perform. +/// operation the CLI will perform. /// /// # Usage /// @@ -76,4 +74,4 @@ mod test { let cli = Cli::parse_from(args); assert!(matches!(cli.command, Command::Push)); } -} \ No newline at end of file +} diff --git a/src/types/mod.rs b/src/types/mod.rs index b9f9274..4b28d6f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,2 +1,2 @@ pub mod commands; -pub mod repository; \ No newline at end of file +pub mod repository; diff --git a/src/types/repository.rs b/src/types/repository.rs index 1ce1961..064cc07 100644 --- a/src/types/repository.rs +++ b/src/types/repository.rs @@ -8,7 +8,7 @@ use git2::{Error, Repository}; /// # Returns /// /// * `Ok(Repository)` - The discovered Git repository object. -/// * `Err(Error)` - If no repository could be found or an error occurred while accessing it. +/// * `Err(Error)` - If no repository could be found, or an error occurred while accessing it. pub fn get_current_repository() -> Result { Repository::discover(".") } @@ -22,9 +22,11 @@ mod test { match get_current_repository() { Err(e) => panic!("{}", e.message()), Ok(repo) => { - println!("{:?}", repo.workdir().unwrap().file_name().unwrap().to_str()); - assert!(true) + println!( + "{:?}", + repo.workdir().unwrap().file_name().unwrap().to_str() + ); } } } -} \ No newline at end of file +}