|
1 | 1 | use super::{repository::repo, RepoPath}; |
2 | | -use crate::error::Result; |
3 | | -pub use git2_hooks::PrepareCommitMsgSource; |
| 2 | +use crate::{ |
| 3 | + error::Result, sync::remotes::tags::tags_missing_remote, |
| 4 | +}; |
| 5 | +use git2::BranchType; |
| 6 | +pub use git2_hooks::{PrePushRef, PrepareCommitMsgSource}; |
4 | 7 | use scopetime::scope_time; |
5 | 8 |
|
6 | 9 | /// |
@@ -30,6 +33,38 @@ impl From<git2_hooks::HookResult> for HookResult { |
30 | 33 | } |
31 | 34 | } |
32 | 35 |
|
| 36 | +fn pre_push_branch_update( |
| 37 | + repo: &git2::Repository, |
| 38 | + remote: Option<&str>, |
| 39 | + branch_name: &str, |
| 40 | + remote_branch_name: Option<&str>, |
| 41 | + delete: bool, |
| 42 | +) -> PrePushRef { |
| 43 | + let local_ref = format!("refs/heads/{branch_name}"); |
| 44 | + let local_oid = (!delete) |
| 45 | + .then(|| { |
| 46 | + repo.find_branch(branch_name, BranchType::Local) |
| 47 | + .ok() |
| 48 | + .and_then(|branch| branch.get().peel_to_commit().ok()) |
| 49 | + .map(|commit| commit.id()) |
| 50 | + }) |
| 51 | + .flatten(); |
| 52 | + |
| 53 | + let remote_branch = remote_branch_name.unwrap_or(branch_name); |
| 54 | + let remote_ref = format!("refs/heads/{remote_branch}"); |
| 55 | + |
| 56 | + let remote_oid = remote.and_then(|remote_name| { |
| 57 | + repo.find_reference(&format!( |
| 58 | + "refs/remotes/{remote_name}/{remote_branch}" |
| 59 | + )) |
| 60 | + .ok() |
| 61 | + .and_then(|r| r.peel_to_commit().ok()) |
| 62 | + .map(|c| c.id()) |
| 63 | + }); |
| 64 | + |
| 65 | + PrePushRef::new(local_ref, local_oid, remote_ref, remote_oid) |
| 66 | +} |
| 67 | + |
33 | 68 | /// see `git2_hooks::hooks_commit_msg` |
34 | 69 | pub fn hooks_commit_msg( |
35 | 70 | repo_path: &RepoPath, |
@@ -81,22 +116,81 @@ pub fn hooks_pre_push( |
81 | 116 | repo_path: &RepoPath, |
82 | 117 | remote: Option<&str>, |
83 | 118 | url: &str, |
84 | | - branch_name: Option<&str>, |
85 | | - remote_branch_name: Option<&str>, |
| 119 | + updates: &[PrePushRef], |
86 | 120 | ) -> Result<HookResult> { |
87 | 121 | scope_time!("hooks_pre_push"); |
88 | 122 |
|
89 | 123 | let repo = repo(repo_path)?; |
90 | 124 |
|
91 | | - Ok(git2_hooks::hooks_pre_push( |
92 | | - &repo, |
93 | | - None, |
94 | | - remote, |
95 | | - url, |
96 | | - branch_name, |
97 | | - remote_branch_name, |
98 | | - )? |
99 | | - .into()) |
| 125 | + Ok( |
| 126 | + git2_hooks::hooks_pre_push( |
| 127 | + &repo, None, remote, url, updates, |
| 128 | + )? |
| 129 | + .into(), |
| 130 | + ) |
| 131 | +} |
| 132 | + |
| 133 | +/// Build a single pre-push update line for a branch. |
| 134 | +pub fn pre_push_branch_update( |
| 135 | + repo_path: &RepoPath, |
| 136 | + remote: Option<&str>, |
| 137 | + branch_name: &str, |
| 138 | + remote_branch_name: Option<&str>, |
| 139 | + delete: bool, |
| 140 | +) -> Result<PrePushRef> { |
| 141 | + let repo = repo(repo_path)?; |
| 142 | + let local_ref = format!("refs/heads/{branch_name}"); |
| 143 | + let local_oid = (!delete) |
| 144 | + .then(|| { |
| 145 | + repo.find_branch(branch_name, BranchType::Local) |
| 146 | + .ok() |
| 147 | + .and_then(|branch| branch.get().peel_to_commit().ok()) |
| 148 | + .map(|commit| commit.id()) |
| 149 | + }) |
| 150 | + .flatten(); |
| 151 | + |
| 152 | + let remote_branch = remote_branch_name.unwrap_or(branch_name); |
| 153 | + let remote_ref = format!("refs/heads/{remote_branch}"); |
| 154 | + |
| 155 | + let remote_oid = remote.and_then(|remote_name| { |
| 156 | + repo.find_reference(&format!( |
| 157 | + "refs/remotes/{remote_name}/{remote_branch}" |
| 158 | + )) |
| 159 | + .ok() |
| 160 | + .and_then(|r| r.peel_to_commit().ok()) |
| 161 | + .map(|c| c.id()) |
| 162 | + }); |
| 163 | + |
| 164 | + Ok(PrePushRef::new( |
| 165 | + local_ref, local_oid, remote_ref, remote_oid, |
| 166 | + )) |
| 167 | +} |
| 168 | + |
| 169 | +/// Build pre-push updates for tags that are missing on the remote. |
| 170 | +pub fn pre_push_tag_updates( |
| 171 | + repo_path: &RepoPath, |
| 172 | + remote: &str, |
| 173 | +) -> Result<Vec<PrePushRef>> { |
| 174 | + let repo = repo(repo_path)?; |
| 175 | + let tags = tags_missing_remote(repo_path, remote, None)?; |
| 176 | + let mut updates = Vec::with_capacity(tags.len()); |
| 177 | + |
| 178 | + for tag_ref in tags { |
| 179 | + if let Ok(reference) = repo.find_reference(&tag_ref) { |
| 180 | + let tag_oid = reference.target().or_else(|| { |
| 181 | + reference.peel_to_commit().ok().map(|c| c.id()) |
| 182 | + }); |
| 183 | + |
| 184 | + updates.push(PrePushRef::new( |
| 185 | + tag_ref.clone(), |
| 186 | + tag_oid, |
| 187 | + tag_ref, |
| 188 | + None, |
| 189 | + )); |
| 190 | + } |
| 191 | + } |
| 192 | + |
| 193 | + Ok(updates) |
100 | 194 | } |
101 | 195 |
|
102 | 196 | #[cfg(test)] |
|
0 commit comments