jujutsu_lib/
transaction.rs1use std::sync::Arc;
16
17use crate::backend::Timestamp;
18use crate::dag_walk::closest_common_node;
19use crate::index::ReadonlyIndex;
20use crate::op_store;
21use crate::op_store::OperationMetadata;
22use crate::operation::Operation;
23use crate::repo::{MutableRepo, ReadonlyRepo, Repo, RepoLoader};
24use crate::settings::UserSettings;
25use crate::view::View;
26
27pub struct Transaction {
28 mut_repo: MutableRepo,
29 parent_ops: Vec<Operation>,
30 op_metadata: OperationMetadata,
31 end_time: Option<Timestamp>,
32}
33
34impl Transaction {
35 pub fn new(
36 mut_repo: MutableRepo,
37 user_settings: &UserSettings,
38 description: &str,
39 ) -> Transaction {
40 let parent_ops = vec![mut_repo.base_repo().operation().clone()];
41 let op_metadata = create_op_metadata(user_settings, description.to_string());
42 let end_time = user_settings.operation_timestamp();
43 Transaction {
44 mut_repo,
45 parent_ops,
46 op_metadata,
47 end_time,
48 }
49 }
50
51 pub fn base_repo(&self) -> &Arc<ReadonlyRepo> {
52 self.mut_repo.base_repo()
53 }
54
55 pub fn set_tag(&mut self, key: String, value: String) {
56 self.op_metadata.tags.insert(key, value);
57 }
58
59 pub fn repo(&self) -> &MutableRepo {
60 &self.mut_repo
61 }
62
63 pub fn mut_repo(&mut self) -> &mut MutableRepo {
64 &mut self.mut_repo
65 }
66
67 pub fn merge_operation(&mut self, other_op: Operation) {
68 let ancestor_op = closest_common_node(
69 self.parent_ops.clone(),
70 vec![other_op.clone()],
71 &|op: &Operation| op.parents(),
72 &|op: &Operation| op.id().clone(),
73 )
74 .unwrap();
75 let repo_loader = self.base_repo().loader();
76 let base_repo = repo_loader.load_at(&ancestor_op);
77 let other_repo = repo_loader.load_at(&other_op);
78 self.parent_ops.push(other_op);
79 let merged_repo = self.mut_repo();
80 merged_repo.merge(&base_repo, &other_repo);
81 }
82
83 pub fn commit(self) -> Arc<ReadonlyRepo> {
85 self.write().publish()
86 }
87
88 pub fn write(mut self) -> UnpublishedOperation {
92 let mut_repo = self.mut_repo;
93 assert!(
95 !mut_repo.has_rewrites(),
96 "BUG: Descendants have not been rebased after the last rewrites."
97 );
98 let base_repo = mut_repo.base_repo().clone();
99 let (mut_index, view) = mut_repo.consume();
100 let index = base_repo.index_store().write_index(mut_index).unwrap();
101
102 let view_id = base_repo.op_store().write_view(view.store_view()).unwrap();
103 self.op_metadata.end_time = self.end_time.unwrap_or_else(Timestamp::now);
104 let parents = self.parent_ops.iter().map(|op| op.id().clone()).collect();
105 let store_operation = op_store::Operation {
106 view_id,
107 parents,
108 metadata: self.op_metadata,
109 };
110 let new_op_id = base_repo
111 .op_store()
112 .write_operation(&store_operation)
113 .unwrap();
114 let operation = Operation::new(base_repo.op_store().clone(), new_op_id, store_operation);
115
116 base_repo
117 .index_store()
118 .associate_file_with_operation(&index, operation.id())
119 .unwrap();
120 UnpublishedOperation::new(base_repo.loader(), operation, view, index)
121 }
122}
123
124pub fn create_op_metadata(user_settings: &UserSettings, description: String) -> OperationMetadata {
125 let start_time = user_settings
126 .operation_timestamp()
127 .unwrap_or_else(Timestamp::now);
128 let end_time = start_time.clone();
129 let hostname = user_settings.operation_hostname();
130 let username = user_settings.operation_username();
131 OperationMetadata {
132 start_time,
133 end_time,
134 description,
135 hostname,
136 username,
137 tags: Default::default(),
138 }
139}
140
141struct NewRepoData {
142 operation: Operation,
143 view: View,
144 index: Arc<ReadonlyIndex>,
145}
146
147pub struct UnpublishedOperation {
148 repo_loader: RepoLoader,
149 data: Option<NewRepoData>,
150 closed: bool,
151}
152
153impl UnpublishedOperation {
154 fn new(
155 repo_loader: RepoLoader,
156 operation: Operation,
157 view: View,
158 index: Arc<ReadonlyIndex>,
159 ) -> Self {
160 let data = Some(NewRepoData {
161 operation,
162 view,
163 index,
164 });
165 UnpublishedOperation {
166 repo_loader,
167 data,
168 closed: false,
169 }
170 }
171
172 pub fn operation(&self) -> &Operation {
173 &self.data.as_ref().unwrap().operation
174 }
175
176 pub fn publish(mut self) -> Arc<ReadonlyRepo> {
177 let data = self.data.take().unwrap();
178 self.repo_loader
179 .op_heads_store()
180 .lock()
181 .promote_new_op(&data.operation);
182 let repo = self
183 .repo_loader
184 .create_from(data.operation, data.view, data.index);
185 self.closed = true;
186 repo
187 }
188
189 pub fn leave_unpublished(mut self) -> Arc<ReadonlyRepo> {
190 let data = self.data.take().unwrap();
191 let repo = self
192 .repo_loader
193 .create_from(data.operation, data.view, data.index);
194 self.closed = true;
195 repo
196 }
197}
198
199impl Drop for UnpublishedOperation {
200 fn drop(&mut self) {
201 if !self.closed && !std::thread::panicking() {
202 eprintln!("BUG: UnpublishedOperation was dropped without being closed.");
203 }
204 }
205}