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