1#![allow(missing_docs)]
16
17use std::sync::Arc;
18
19use pollster::FutureExt as _;
20
21use crate::backend;
22use crate::backend::BackendResult;
23use crate::backend::ChangeId;
24use crate::backend::CommitId;
25use crate::backend::MergedTreeId;
26use crate::backend::Signature;
27use crate::commit::is_backend_commit_empty;
28use crate::commit::Commit;
29use crate::repo::MutableRepo;
30use crate::repo::Repo;
31use crate::settings::JJRng;
32use crate::settings::SignSettings;
33use crate::settings::UserSettings;
34use crate::signing::SignBehavior;
35use crate::store::Store;
36
37#[must_use]
38pub struct CommitBuilder<'repo> {
39 mut_repo: &'repo mut MutableRepo,
40 inner: DetachedCommitBuilder,
41}
42
43impl CommitBuilder<'_> {
44 pub fn detach(self) -> DetachedCommitBuilder {
47 self.inner
48 }
49
50 pub fn parents(&self) -> &[CommitId] {
51 self.inner.parents()
52 }
53
54 pub fn set_parents(mut self, parents: Vec<CommitId>) -> Self {
55 self.inner.set_parents(parents);
56 self
57 }
58
59 pub fn predecessors(&self) -> &[CommitId] {
60 self.inner.predecessors()
61 }
62
63 pub fn set_predecessors(mut self, predecessors: Vec<CommitId>) -> Self {
64 self.inner.set_predecessors(predecessors);
65 self
66 }
67
68 pub fn tree_id(&self) -> &MergedTreeId {
69 self.inner.tree_id()
70 }
71
72 pub fn set_tree_id(mut self, tree_id: MergedTreeId) -> Self {
73 self.inner.set_tree_id(tree_id);
74 self
75 }
76
77 pub fn is_empty(&self) -> BackendResult<bool> {
79 self.inner.is_empty(self.mut_repo)
80 }
81
82 pub fn change_id(&self) -> &ChangeId {
83 self.inner.change_id()
84 }
85
86 pub fn set_change_id(mut self, change_id: ChangeId) -> Self {
87 self.inner.set_change_id(change_id);
88 self
89 }
90
91 pub fn generate_new_change_id(mut self) -> Self {
92 self.inner.generate_new_change_id();
93 self
94 }
95
96 pub fn description(&self) -> &str {
97 self.inner.description()
98 }
99
100 pub fn set_description(mut self, description: impl Into<String>) -> Self {
101 self.inner.set_description(description);
102 self
103 }
104
105 pub fn author(&self) -> &Signature {
106 self.inner.author()
107 }
108
109 pub fn set_author(mut self, author: Signature) -> Self {
110 self.inner.set_author(author);
111 self
112 }
113
114 pub fn committer(&self) -> &Signature {
115 self.inner.committer()
116 }
117
118 pub fn set_committer(mut self, committer: Signature) -> Self {
119 self.inner.set_committer(committer);
120 self
121 }
122
123 pub fn is_discardable(&self) -> BackendResult<bool> {
125 self.inner.is_discardable(self.mut_repo)
126 }
127
128 pub fn sign_settings(&self) -> &SignSettings {
129 self.inner.sign_settings()
130 }
131
132 pub fn set_sign_behavior(mut self, sign_behavior: SignBehavior) -> Self {
133 self.inner.set_sign_behavior(sign_behavior);
134 self
135 }
136
137 pub fn set_sign_key(mut self, sign_key: String) -> Self {
138 self.inner.set_sign_key(sign_key);
139 self
140 }
141
142 pub fn clear_sign_key(mut self) -> Self {
143 self.inner.clear_sign_key();
144 self
145 }
146
147 pub fn write(self) -> BackendResult<Commit> {
148 self.inner.write(self.mut_repo)
149 }
150
151 pub fn abandon(self) {
154 self.inner.abandon(self.mut_repo);
155 }
156}
157
158#[derive(Debug)]
160pub struct DetachedCommitBuilder {
161 store: Arc<Store>,
162 rng: Arc<JJRng>,
163 commit: backend::Commit,
164 rewrite_source: Option<Commit>,
165 sign_settings: SignSettings,
166}
167
168impl DetachedCommitBuilder {
169 pub(crate) fn for_new_commit(
171 repo: &dyn Repo,
172 settings: &UserSettings,
173 parents: Vec<CommitId>,
174 tree_id: MergedTreeId,
175 ) -> Self {
176 let store = repo.store().clone();
177 let signature = settings.signature();
178 assert!(!parents.is_empty());
179 let rng = settings.get_rng();
180 let change_id = rng.new_change_id(store.change_id_length());
181 let commit = backend::Commit {
182 parents,
183 predecessors: vec![],
184 root_tree: tree_id,
185 change_id,
186 description: String::new(),
187 author: signature.clone(),
188 committer: signature,
189 secure_sig: None,
190 };
191 DetachedCommitBuilder {
192 store,
193 rng,
194 commit,
195 rewrite_source: None,
196 sign_settings: settings.sign_settings(),
197 }
198 }
199
200 pub(crate) fn for_rewrite_from(
203 repo: &dyn Repo,
204 settings: &UserSettings,
205 predecessor: &Commit,
206 ) -> Self {
207 let store = repo.store().clone();
208 let mut commit = backend::Commit::clone(predecessor.store_commit());
209 commit.predecessors = vec![predecessor.id().clone()];
210 commit.committer = settings.signature();
211 if commit.author.name.is_empty()
214 || commit.author.name == UserSettings::USER_NAME_PLACEHOLDER
215 {
216 commit.author.name.clone_from(&commit.committer.name);
217 }
218 if commit.author.email.is_empty()
219 || commit.author.email == UserSettings::USER_EMAIL_PLACEHOLDER
220 {
221 commit.author.email.clone_from(&commit.committer.email);
222 }
223
224 if commit.author.name == commit.committer.name
228 && commit.author.email == commit.committer.email
229 && predecessor.is_discardable(repo).unwrap_or_default()
230 {
231 commit.author.timestamp = commit.committer.timestamp;
232 }
233
234 DetachedCommitBuilder {
235 store,
236 commit,
237 rng: settings.get_rng(),
238 rewrite_source: Some(predecessor.clone()),
239 sign_settings: settings.sign_settings(),
240 }
241 }
242
243 pub fn attach(self, mut_repo: &mut MutableRepo) -> CommitBuilder<'_> {
245 assert!(Arc::ptr_eq(&self.store, mut_repo.store()));
246 CommitBuilder {
247 mut_repo,
248 inner: self,
249 }
250 }
251
252 pub fn parents(&self) -> &[CommitId] {
253 &self.commit.parents
254 }
255
256 pub fn set_parents(&mut self, parents: Vec<CommitId>) -> &mut Self {
257 assert!(!parents.is_empty());
258 self.commit.parents = parents;
259 self
260 }
261
262 pub fn predecessors(&self) -> &[CommitId] {
263 &self.commit.predecessors
264 }
265
266 pub fn set_predecessors(&mut self, predecessors: Vec<CommitId>) -> &mut Self {
267 self.commit.predecessors = predecessors;
268 self
269 }
270
271 pub fn tree_id(&self) -> &MergedTreeId {
272 &self.commit.root_tree
273 }
274
275 pub fn set_tree_id(&mut self, tree_id: MergedTreeId) -> &mut Self {
276 self.commit.root_tree = tree_id;
277 self
278 }
279
280 pub fn is_empty(&self, repo: &dyn Repo) -> BackendResult<bool> {
282 is_backend_commit_empty(repo, &self.store, &self.commit)
283 }
284
285 pub fn change_id(&self) -> &ChangeId {
286 &self.commit.change_id
287 }
288
289 pub fn set_change_id(&mut self, change_id: ChangeId) -> &mut Self {
290 self.commit.change_id = change_id;
291 self
292 }
293
294 pub fn generate_new_change_id(&mut self) -> &mut Self {
295 self.commit.change_id = self.rng.new_change_id(self.store.change_id_length());
296 self
297 }
298
299 pub fn description(&self) -> &str {
300 &self.commit.description
301 }
302
303 pub fn set_description(&mut self, description: impl Into<String>) -> &mut Self {
304 self.commit.description = description.into();
305 self
306 }
307
308 pub fn author(&self) -> &Signature {
309 &self.commit.author
310 }
311
312 pub fn set_author(&mut self, author: Signature) -> &mut Self {
313 self.commit.author = author;
314 self
315 }
316
317 pub fn committer(&self) -> &Signature {
318 &self.commit.committer
319 }
320
321 pub fn set_committer(&mut self, committer: Signature) -> &mut Self {
322 self.commit.committer = committer;
323 self
324 }
325
326 pub fn is_discardable(&self, repo: &dyn Repo) -> BackendResult<bool> {
328 Ok(self.description().is_empty() && self.is_empty(repo)?)
329 }
330
331 pub fn sign_settings(&self) -> &SignSettings {
332 &self.sign_settings
333 }
334
335 pub fn set_sign_behavior(&mut self, sign_behavior: SignBehavior) -> &mut Self {
336 self.sign_settings.behavior = sign_behavior;
337 self
338 }
339
340 pub fn set_sign_key(&mut self, sign_key: String) -> &mut Self {
341 self.sign_settings.key = Some(sign_key);
342 self
343 }
344
345 pub fn clear_sign_key(&mut self) -> &mut Self {
346 self.sign_settings.key = None;
347 self
348 }
349
350 pub fn write(self, mut_repo: &mut MutableRepo) -> BackendResult<Commit> {
352 let predecessors = self.commit.predecessors.clone();
353 let commit = write_to_store(&self.store, self.commit, &self.sign_settings)?;
354 mut_repo.add_head(&commit)?;
355 mut_repo.set_predecessors(commit.id().clone(), predecessors);
356 if let Some(rewrite_source) = self.rewrite_source {
357 if rewrite_source.change_id() == commit.change_id() {
358 mut_repo.set_rewritten_commit(rewrite_source.id().clone(), commit.id().clone());
359 }
360 }
361 Ok(commit)
362 }
363
364 pub fn write_hidden(&self) -> BackendResult<Commit> {
369 write_to_store(&self.store, self.commit.clone(), &self.sign_settings)
370 }
371
372 pub fn abandon(self, mut_repo: &mut MutableRepo) {
377 let commit = self.commit;
378 if let Some(rewrite_source) = &self.rewrite_source {
379 if rewrite_source.change_id() == &commit.change_id {
380 mut_repo.record_abandoned_commit_with_parents(
381 rewrite_source.id().clone(),
382 commit.parents,
383 );
384 }
385 }
386 }
387}
388
389fn write_to_store(
390 store: &Arc<Store>,
391 mut commit: backend::Commit,
392 sign_settings: &SignSettings,
393) -> BackendResult<Commit> {
394 let should_sign = store.signer().can_sign() && sign_settings.should_sign(&commit);
395 let sign_fn = |data: &[u8]| store.signer().sign(data, sign_settings.key.as_deref());
396
397 commit.secure_sig = None;
401
402 store
403 .write_commit(commit, should_sign.then_some(&mut &sign_fn))
404 .block_on()
405}