A file-based task manager
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add git-setup command to add .tsk/ to gitignore or .git/info/exclude

- tsk-21: Add tsk git-setup command with -g flag for .gitignore support
- Add tests for git setup functionality

+82 -2
+41 -1
src/main.rs
··· 11 11 use std::path::PathBuf; 12 12 use std::process::exit; 13 13 use std::str::FromStr as _; 14 - use std::{env::current_dir, io::Read}; 14 + use std::{env::current_dir, fs::OpenOptions, io::Read}; 15 15 use task::ParsedLink; 16 16 use workspace::{Id, Task, TaskIdentifier, Workspace}; 17 17 ··· 183 183 #[command(subcommand)] 184 184 action: RemoteAction, 185 185 }, 186 + 187 + /// Sets up git integration by adding .tsk/ to .git/info/exclude or .gitignore. 188 + GitSetup { 189 + /// Use .gitignore instead of .git/info/exclude. 190 + #[arg(short = 'g', default_value_t = false)] 191 + gitignore: bool, 192 + }, 186 193 } 187 194 188 195 #[derive(Subcommand)] ··· 309 316 Commands::Deprioritize { task_id } => command_deprioritize(dir, task_id), 310 317 Commands::Clean => command_clean(dir), 311 318 Commands::Remote { action } => command_remote(dir, action), 319 + Commands::GitSetup { gitignore } => command_git_setup(dir, gitignore), 312 320 }; 313 321 let result = var_name; 314 322 match result { ··· 576 584 } 577 585 Ok(()) 578 586 } 587 + 588 + fn command_git_setup(dir: PathBuf, use_gitignore: bool) -> Result<()> { 589 + let workspace = Workspace::from_path(dir)?; 590 + let git_dir = workspace.path.join(".git"); 591 + if !git_dir.exists() { 592 + eprintln!("No .git directory found at workspace root."); 593 + exit(1); 594 + } 595 + let (ignore_file, label) = if use_gitignore { 596 + (workspace.path.join(".gitignore"), ".gitignore") 597 + } else { 598 + let info_dir = git_dir.join("info"); 599 + std::fs::create_dir_all(&info_dir)?; 600 + (info_dir.join("exclude"), ".git/info/exclude") 601 + }; 602 + let content = if ignore_file.exists() { 603 + std::fs::read_to_string(&ignore_file)? 604 + } else { 605 + String::new() 606 + }; 607 + if content.lines().any(|line| line.trim() == ".tsk/") { 608 + eprintln!(".tsk/ is already in {label}."); 609 + return Ok(()); 610 + } 611 + let mut file = OpenOptions::new() 612 + .append(true) 613 + .create(true) 614 + .open(&ignore_file)?; 615 + writeln!(file, ".tsk/")?; 616 + eprintln!("Added .tsk/ to {label}."); 617 + Ok(()) 618 + }
+41 -1
src/workspace.rs
··· 75 75 pub struct Workspace { 76 76 /// The path to the workspace root, excluding the .tsk directory. This should *contain* the 77 77 /// .tsk directory. 78 - path: PathBuf, 78 + pub path: PathBuf, 79 79 } 80 80 81 81 #[derive(Clone, Debug, Eq, PartialEq)] ··· 791 791 path: PathBuf::from("/path/to/jira"), 792 792 }; 793 793 assert_eq!("jira\t/path/to/jira", remote.to_string()); 794 + } 795 + 796 + #[test] 797 + fn test_git_setup_exclude() { 798 + let (_dir, workspace) = setup_test_workspace(); 799 + let git_dir = workspace.path.join(".git"); 800 + let info_dir = git_dir.join("info"); 801 + fs::create_dir_all(&info_dir).unwrap(); 802 + 803 + let exclude_path = info_dir.join("exclude"); 804 + assert!(!exclude_path.exists()); 805 + 806 + let content = std::fs::read_to_string(&exclude_path).unwrap_or_default(); 807 + assert!(!content.contains(".tsk/")); 808 + 809 + // Simulate git_setup logic 810 + let mut file = OpenOptions::new() 811 + .append(true) 812 + .create(true) 813 + .open(&exclude_path) 814 + .unwrap(); 815 + writeln!(file, ".tsk/").unwrap(); 816 + 817 + let content = std::fs::read_to_string(&exclude_path).unwrap(); 818 + assert!(content.contains(".tsk/")); 819 + } 820 + 821 + #[test] 822 + fn test_git_setup_no_duplicate() { 823 + let (_dir, workspace) = setup_test_workspace(); 824 + let git_dir = workspace.path.join(".git"); 825 + let info_dir = git_dir.join("info"); 826 + fs::create_dir_all(&info_dir).unwrap(); 827 + 828 + let exclude_path = info_dir.join("exclude"); 829 + fs::write(&exclude_path, ".tsk/\nother/").unwrap(); 830 + 831 + let content = std::fs::read_to_string(&exclude_path).unwrap(); 832 + let already_present = content.lines().any(|line| line.trim() == ".tsk/"); 833 + assert!(already_present); 794 834 } 795 835 }