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