mod.rs (4622B)
1 use std::{io::Write, path::Path}; 2 3 use color_eyre::eyre::Result; 4 use git2::{Oid, Repository, Signature}; 5 use tracing::{info, warn}; 6 7 /// Initialize the git repository 8 /// 9 /// If the git repository already exists, open it. Otherwise, create it. 10 /// 11 /// If the git repository is created, generate the README.md file from the template. 12 /// 13 /// # Arguments 14 /// 15 /// * `git_repo_path` - The path to the git repository 16 /// * `data_url` - The URL to the OSM data server 17 /// * `changeset_url` - The URL to the OSM changeset server 18 /// 19 /// # Returns 20 /// 21 /// * `Result<Repository>` - The git repository 22 pub fn init_git_repository( 23 git_repo_path: &str, 24 data_url: &str, 25 author: &Signature, 26 ) -> Result<Repository> { 27 // Check if the git repo already exists 28 if std::path::Path::new(git_repo_path).exists() { 29 info!("Git repository already exists at {}", git_repo_path); 30 // Open the git repo 31 let repository = Repository::open(git_repo_path)?; 32 33 return Ok(repository); 34 } 35 36 info!("Initializing git repository at {}", git_repo_path); 37 38 // Create the git repo if it doesn't exist 39 let repository = Repository::init(git_repo_path)?; 40 41 generate_readme_from_template(&repository, data_url)?; 42 43 // Commit the README.md file 44 commit( 45 &repository, 46 vec!["README.md".to_string()], 47 vec![], 48 "Create the README.md", 49 author, 50 author, 51 )?; 52 Ok(repository) 53 } 54 55 /// Generate the README.md file from the template and write it to the git repo 56 pub fn generate_readme_from_template(repository: &Repository, data_url: &str) -> Result<()> { 57 let template_file = include_str!("../../templates/README.md"); 58 59 // Replace the template variables with the actual values 60 let template_file = template_file.replace("$server_url", data_url); 61 62 // Get the version of this binary 63 let version = env!("CARGO_PKG_VERSION"); 64 let template_file = template_file.replace("$version", version); 65 66 // Write the README.md file in the git repo (parent of .git directory) 67 let path = repository 68 .path() 69 .parent() 70 .expect("Git repository path is not valid"); 71 let readme_file_path = path.join("README.md"); 72 info!( 73 "Generating README.md file at {}", 74 readme_file_path 75 .to_str() 76 .expect("README.md file path is not valid") 77 ); 78 let mut readme_file = std::fs::File::create(readme_file_path)?; 79 readme_file.write_all(template_file.as_bytes())?; 80 readme_file.sync_all()?; 81 82 info!("README.md file generated"); 83 84 Ok(()) 85 } 86 87 /// Helper for creating a git commit 88 pub fn commit( 89 repository: &Repository, 90 added_or_changed_files: Vec<String>, 91 removed_files: Vec<String>, 92 message: &str, 93 author: &Signature, 94 committer: &Signature, 95 ) -> Result<Oid> { 96 let tree_id = { 97 let mut index = repository.index()?; 98 for file in added_or_changed_files { 99 let file_path = Path::new(&file); 100 let path = if file_path.starts_with(repository.path().parent().unwrap()) { 101 Path::new(&file).strip_prefix(repository.path().parent().unwrap())? 102 } else { 103 Path::new(&file) 104 }; 105 // TODO: I am tired to actually debug this so we just do a sanity check if the file exists 106 if file_path.exists() { 107 index.add_path(path)?; 108 } else { 109 warn!( 110 "File {} does not exist but was meant to be added", 111 path.to_str().unwrap() 112 ); 113 } 114 } 115 for file in removed_files { 116 let file_path = Path::new(&file); 117 let path = if file_path.starts_with(repository.path().parent().unwrap()) { 118 Path::new(&file).strip_prefix(repository.path().parent().unwrap())? 119 } else { 120 Path::new(&file) 121 }; 122 // We check if it was tracked before. If not we don't need to remove it 123 if index.get_path(path, 0).is_some() { 124 index.remove_path(path)?; 125 } 126 } 127 index.write()?; 128 index.write_tree()? 129 }; 130 let tree = repository.find_tree(tree_id)?; 131 let head_id = repository.refname_to_id("HEAD"); 132 if let Ok(head_id) = head_id { 133 let parent = repository.find_commit(head_id)?; 134 135 let oid = repository.commit(Some("HEAD"), author, committer, message, &tree, &[&parent])?; 136 Ok(oid) 137 } else { 138 let oid = repository.commit(Some("HEAD"), author, committer, message, &tree, &[])?; 139 Ok(oid) 140 } 141 }