Create .atomicignore and initialize vault AFTER git import + materialize. This runs post-import so the import's materialize step can't stomp the vault files' tracked state. The sequence is: 1. Create .atomicignore (auto-detect project type) → add → record 2. Create .vault/ with defaults → add → record 3. Status is clean.
(
repo: &mut Repository,
workdir: &std::path::Path,
kind: Option<&str>,
no_vault: bool,
)
| 662 | /// 2. Create .vault/ with defaults → add → record |
| 663 | /// 3. Status is clean. |
| 664 | fn init_atomicignore_and_vault( |
| 665 | repo: &mut Repository, |
| 666 | workdir: &std::path::Path, |
| 667 | kind: Option<&str>, |
| 668 | no_vault: bool, |
| 669 | ) -> CliResult<()> { |
| 670 | // Step 1: .atomicignore |
| 671 | { |
| 672 | let ignore_path = workdir.join(".atomicignore"); |
| 673 | if !ignore_path.exists() { |
| 674 | // Use explicit --kind if provided, otherwise auto-detect from project files |
| 675 | let ignore_content = if let Some(k) = kind { |
| 676 | super::super::init::get_ignore_template(k).unwrap_or(".atomic\n.git\n") |
| 677 | } else if workdir.join("Cargo.toml").exists() { |
| 678 | super::super::init::get_ignore_template("rust").unwrap_or(".atomic\n.git\n") |
| 679 | } else if workdir.join("package.json").exists() { |
| 680 | super::super::init::get_ignore_template("node").unwrap_or(".atomic\n.git\n") |
| 681 | } else if workdir.join("go.mod").exists() { |
| 682 | super::super::init::get_ignore_template("go").unwrap_or(".atomic\n.git\n") |
| 683 | } else if workdir.join("setup.py").exists() || workdir.join("pyproject.toml").exists() { |
| 684 | super::super::init::get_ignore_template("python").unwrap_or(".atomic\n.git\n") |
| 685 | } else { |
| 686 | ".atomic\n.git\n" |
| 687 | }; |
| 688 | let _ = std::fs::write(&ignore_path, ignore_content); |
| 689 | } |
| 690 | |
| 691 | let _ = repo.add( |
| 692 | ".atomicignore", |
| 693 | atomic_repository::TrackingOptions::default(), |
| 694 | ); |
| 695 | let header = atomic_core::change::ChangeHeader::new("Initialize repository"); |
| 696 | match repo.record(header, atomic_repository::RecordOptions::default()) { |
| 697 | Ok(_) => print_info("Recorded .atomicignore"), |
| 698 | Err(atomic_repository::RecordError::NothingToRecord) => {} |
| 699 | Err(e) => log::warn!("Failed to record .atomicignore: {}", e), |
| 700 | } |
| 701 | } |
| 702 | |
| 703 | // Step 2: Vault (unless --no-vault) |
| 704 | if no_vault { |
| 705 | return Ok(()); |
| 706 | } |
| 707 | |
| 708 | match repo.init_vault() { |
| 709 | Ok(()) => { |
| 710 | print_info("Initialized vault at .vault/"); |
| 711 | |
| 712 | // Add all vault files |
| 713 | fn add_dir_recursive(repo: &Repository, dir: &std::path::Path) { |
| 714 | if let Ok(entries) = std::fs::read_dir(dir) { |
| 715 | for entry in entries.flatten() { |
| 716 | let path = entry.path(); |
| 717 | if path.is_dir() { |
| 718 | add_dir_recursive(repo, &path); |
| 719 | } else if path.is_file() { |
| 720 | if let Ok(rel) = path.strip_prefix(repo.root()) { |
| 721 | let rel_str = rel.to_string_lossy().replace('\\', "/"); |
no test coverage detected