Skip to content

Commit a798a36

Browse files
authored
Switch to single-threaded Tokio runtime (#233)
1 parent 4e4a37f commit a798a36

File tree

5 files changed

+66
-63
lines changed

5 files changed

+66
-63
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ secrecy = { version = "0.10.3", default-features = false }
2929
serde = "^1.0.219"
3030
textwrap = "^0.16.2"
3131
thiserror = "^2.0.16"
32-
tokio = { version = "^1.47.1", features = ["macros", "process", "rt-multi-thread", "time"] }
32+
tokio = { version = "^1.47.1", features = ["macros", "process", "rt", "time"] }
3333
unicode-normalization = "^0.1.24"

src/commands/amend.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ pub async fn amend(
4747
.iter()
4848
.rev()
4949
.map(|pc: &PreparedCommit| {
50-
pc.pull_request_number
51-
.map(|number| tokio::spawn(gh.clone().get_pull_request(number)))
50+
pc.pull_request_number.map(|number| {
51+
tokio::task::spawn_local(gh.clone().get_pull_request(number))
52+
})
5253
})
5354
.collect();
5455

src/commands/diff.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub async fn diff(
106106
// the rewrite_commit_messages step. This is not a problem for opts.all as
107107
// it only ever has a single commit to update, and so nothing after it.
108108
let revs_to_pr = match (opts.refs.as_deref(), opts.all) {
109-
(Some(refs), false) => Some(get_oids(refs, &git.repo())?),
109+
(Some(refs), false) => Some(get_oids(refs, git.repo())?),
110110
(Some(_), true) => {
111111
return Err(Error::new("Do not use --refs with --all"))
112112
}
@@ -132,7 +132,9 @@ pub async fn diff(
132132
{
133133
// We are going to want to look at this pull request below.
134134
pc.pull_request_number.map(|number| {
135-
tokio::spawn(gh.clone().get_pull_request(number))
135+
tokio::task::spawn_local(
136+
gh.clone().get_pull_request(number),
137+
)
136138
})
137139
} else {
138140
// We will be skipping this commit below, because we have as set

src/git.rs

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,31 @@ pub struct PreparedCommit {
2929

3030
#[derive(Clone)]
3131
pub struct Git {
32-
repo: std::sync::Arc<std::sync::Mutex<git2::Repository>>,
33-
hooks: std::sync::Arc<std::sync::Mutex<git2_ext::hooks::Hooks>>,
32+
repo: std::sync::Arc<git2::Repository>,
33+
hooks: std::sync::Arc<git2_ext::hooks::Hooks>,
3434
}
3535

3636
impl Git {
3737
pub fn new(repo: git2::Repository) -> Self {
3838
Self {
39-
hooks: std::sync::Arc::new(std::sync::Mutex::new(
39+
hooks: std::sync::Arc::new(
4040
git2_ext::hooks::Hooks::with_repo(&repo).unwrap(),
41-
)),
42-
repo: std::sync::Arc::new(std::sync::Mutex::new(repo)),
41+
),
42+
#[allow(clippy::arc_with_non_send_sync)]
43+
repo: std::sync::Arc::new(repo),
4344
}
4445
}
4546

46-
pub fn repo(&self) -> std::sync::MutexGuard<'_, git2::Repository> {
47-
self.repo.lock().expect("poisoned mutex")
47+
pub fn repo(&self) -> &git2::Repository {
48+
self.repo.as_ref()
4849
}
4950

50-
fn hooks(&self) -> std::sync::MutexGuard<'_, git2_ext::hooks::Hooks> {
51-
self.hooks.lock().expect("poisoned mutex")
51+
fn hooks(&self) -> &git2_ext::hooks::Hooks {
52+
self.hooks.as_ref()
5253
}
5354

5455
pub fn get_commit_oids(&self, master_ref: &str) -> Result<Vec<Oid>> {
55-
let repo = self.repo();
56-
let mut walk = repo.revwalk()?;
56+
let mut walk = self.repo.revwalk()?;
5757
walk.set_sorting(git2::Sort::TOPOLOGICAL.union(git2::Sort::REVERSE))?;
5858
walk.push_head()?;
5959
walk.hide_ref(master_ref)?;
@@ -84,11 +84,10 @@ impl Git {
8484
let mut updating = false;
8585
let mut message: String;
8686
let first_parent = commits[0].parent_oid;
87-
let repo = self.repo();
8887
let hooks = self.hooks();
8988

9089
for prepared_commit in commits.iter_mut() {
91-
let commit = repo.find_commit(prepared_commit.oid)?;
90+
let commit = self.repo.find_commit(prepared_commit.oid)?;
9291
if limit != Some(0) {
9392
message = build_commit_message(&prepared_commit.message);
9493
if Some(&message[..]) != commit.message() {
@@ -104,16 +103,18 @@ impl Git {
104103
limit = limit.map(|n| if n > 0 { n - 1 } else { 0 });
105104

106105
if updating {
107-
let new_oid = repo.commit(
106+
let new_oid = self.repo.commit(
108107
None,
109108
&commit.author(),
110109
&commit.committer(),
111110
&message[..],
112111
&commit.tree()?,
113-
&[&repo.find_commit(parent_oid.unwrap_or(first_parent))?],
112+
&[&self
113+
.repo
114+
.find_commit(parent_oid.unwrap_or(first_parent))?],
114115
)?;
115116
hooks.run_post_rewrite_rebase(
116-
&repo,
117+
self.repo.as_ref(),
117118
&[(prepared_commit.oid, new_oid)],
118119
);
119120
prepared_commit.oid = new_oid;
@@ -125,7 +126,8 @@ impl Git {
125126

126127
if updating {
127128
if let Some(oid) = parent_oid {
128-
repo.find_reference("HEAD")?
129+
self.repo
130+
.find_reference("HEAD")?
129131
.resolve()?
130132
.set_target(oid, "spr updated commit messages")?;
131133
}
@@ -142,20 +144,23 @@ impl Git {
142144
if commits.is_empty() {
143145
return Ok(());
144146
}
145-
let repo = self.repo();
146147
let hooks = self.hooks();
147148

148149
for prepared_commit in commits.iter_mut() {
149-
let new_parent_commit = repo.find_commit(new_parent_oid)?;
150-
let commit = repo.find_commit(prepared_commit.oid)?;
150+
let new_parent_commit = self.repo.find_commit(new_parent_oid)?;
151+
let commit = self.repo.find_commit(prepared_commit.oid)?;
151152

152-
let mut index =
153-
repo.cherrypick_commit(&commit, &new_parent_commit, 0, None)?;
153+
let mut index = self.repo.cherrypick_commit(
154+
&commit,
155+
&new_parent_commit,
156+
0,
157+
None,
158+
)?;
154159
if index.has_conflicts() {
155160
return Err(Error::new("Rebase failed due to merge conflicts"));
156161
}
157162

158-
let tree_oid = index.write_tree_to(&repo)?;
163+
let tree_oid = index.write_tree_to(self.repo.as_ref())?;
159164
if tree_oid == new_parent_commit.tree_id() {
160165
// Rebasing makes this an empty commit. This is probably because
161166
// we just landed this commit. So we should run a hook as this
@@ -165,14 +170,14 @@ impl Git {
165170
// general not an unreasoanble thing for a rebase, ala git
166171
// rebase --interactive and fixups etc.
167172
hooks.run_post_rewrite_rebase(
168-
&repo,
173+
self.repo.as_ref(),
169174
&[(prepared_commit.oid, new_parent_oid)],
170175
);
171176
continue;
172177
}
173-
let tree = repo.find_tree(tree_oid)?;
178+
let tree = self.repo.find_tree(tree_oid)?;
174179

175-
new_parent_oid = repo.commit(
180+
new_parent_oid = self.repo.commit(
176181
None,
177182
&commit.author(),
178183
&commit.committer(),
@@ -181,17 +186,17 @@ impl Git {
181186
&[&new_parent_commit],
182187
)?;
183188
hooks.run_post_rewrite_rebase(
184-
&repo,
189+
self.repo.as_ref(),
185190
&[(prepared_commit.oid, new_parent_oid)],
186191
);
187192
}
188193

189194
let new_oid = new_parent_oid;
190-
let new_commit = repo.find_commit(new_oid)?;
195+
let new_commit = self.repo.find_commit(new_oid)?;
191196

192197
// Get and resolve the HEAD reference. This will be either a reference
193198
// to a branch ('refs/heads/...') or 'HEAD' if the head is detached.
194-
let mut reference = repo.head()?.resolve()?;
199+
let mut reference = self.repo.head()?.resolve()?;
195200

196201
// Checkout the tree of the top commit of the rebased branch. This can
197202
// fail if there are local changes in the worktree that collide with
@@ -203,7 +208,8 @@ impl Git {
203208
// straight away, they will find that it also fails because of local
204209
// worktree changes. Once the user has dealt with those (revert, stash
205210
// or commit), the rebase should work nicely.
206-
repo.checkout_tree(new_commit.as_object(), None)
211+
self.repo
212+
.checkout_tree(new_commit.as_object(), None)
207213
.map_err(Error::from)
208214
.reword(
209215
"Could not check out rebased branch - please rebase manually"
@@ -220,7 +226,7 @@ impl Git {
220226

221227
pub fn head(&self) -> Result<Oid> {
222228
let oid = self
223-
.repo()
229+
.repo
224230
.head()?
225231
.resolve()?
226232
.target()
@@ -230,11 +236,8 @@ impl Git {
230236
}
231237

232238
pub fn resolve_reference(&self, reference: &str) -> Result<Oid> {
233-
let result = self
234-
.repo()
235-
.find_reference(reference)?
236-
.peel_to_commit()?
237-
.id();
239+
let result =
240+
self.repo.find_reference(reference)?.peel_to_commit()?.id();
238241

239242
Ok(result)
240243
}
@@ -302,8 +305,7 @@ impl Git {
302305
config: &Config,
303306
oid: Oid,
304307
) -> Result<PreparedCommit> {
305-
let repo = self.repo();
306-
let commit = repo.find_commit(oid)?;
308+
let commit = self.repo.find_commit(oid)?;
307309

308310
if commit.parent_count() != 1 {
309311
return Err(Error::new("Parent commit count != 1"));
@@ -317,7 +319,6 @@ impl Git {
317319
let short_id =
318320
commit.as_object().short_id()?.as_str().unwrap().to_string();
319321
drop(commit);
320-
drop(repo);
321322

322323
let mut message = parse_message(&message, MessageSection::Title);
323324

@@ -345,7 +346,7 @@ impl Git {
345346

346347
pub fn get_all_ref_names(&self) -> Result<HashSet<String>> {
347348
let result: std::result::Result<HashSet<_>, _> = self
348-
.repo()
349+
.repo
349350
.references()?
350351
.names()
351352
.map(|r| r.map(String::from))
@@ -372,19 +373,20 @@ impl Git {
372373
}
373374

374375
pub fn cherrypick(&self, oid: Oid, base_oid: Oid) -> Result<git2::Index> {
375-
let repo = self.repo();
376-
let commit = repo.find_commit(oid)?;
377-
let base_commit = repo.find_commit(base_oid)?;
376+
let commit = self.repo.find_commit(oid)?;
377+
let base_commit = self.repo.find_commit(base_oid)?;
378378

379-
Ok(repo.cherrypick_commit(&commit, &base_commit, 0, None)?)
379+
Ok(self
380+
.repo
381+
.cherrypick_commit(&commit, &base_commit, 0, None)?)
380382
}
381383

382384
pub fn write_index(&self, mut index: git2::Index) -> Result<Oid> {
383-
Ok(index.write_tree_to(&self.repo())?)
385+
Ok(index.write_tree_to(self.repo.as_ref())?)
384386
}
385387

386388
pub fn get_tree_oid_for_commit(&self, oid: Oid) -> Result<Oid> {
387-
let tree_oid = self.repo().find_commit(oid)?.tree_id();
389+
let tree_oid = self.repo.find_commit(oid)?.tree_id();
388390

389391
Ok(tree_oid)
390392
}
@@ -400,15 +402,14 @@ impl Git {
400402
let mut master_queue = VecDeque::new();
401403
master_ancestors.insert(master_oid);
402404
master_queue.push_back(master_oid);
403-
let repo = self.repo();
404405

405406
while !(commit_oid.is_none() && master_queue.is_empty()) {
406407
if let Some(oid) = commit_oid {
407408
if master_ancestors.contains(&oid) {
408409
return Ok(Some(oid));
409410
}
410411
commit_ancestors.insert(oid);
411-
let commit = repo.find_commit(oid)?;
412+
let commit = self.repo.find_commit(oid)?;
412413
commit_oid = match commit.parent_count() {
413414
0 => None,
414415
l => Some(commit.parent_id(l - 1)?),
@@ -419,7 +420,7 @@ impl Git {
419420
if commit_ancestors.contains(&oid) {
420421
return Ok(Some(oid));
421422
}
422-
let commit = repo.find_commit(oid)?;
423+
let commit = self.repo.find_commit(oid)?;
423424
for oid in commit.parent_ids() {
424425
if !master_ancestors.contains(&oid) {
425426
master_queue.push_back(oid);
@@ -439,12 +440,11 @@ impl Git {
439440
tree_oid: Oid,
440441
parent_oids: &[Oid],
441442
) -> Result<Oid> {
442-
let repo = self.repo();
443-
let original_commit = repo.find_commit(original_commit_oid)?;
444-
let tree = repo.find_tree(tree_oid)?;
443+
let original_commit = self.repo.find_commit(original_commit_oid)?;
444+
let tree = self.repo.find_tree(tree_oid)?;
445445
let parents = parent_oids
446446
.iter()
447-
.map(|oid| repo.find_commit(*oid))
447+
.map(|oid| self.repo.find_commit(*oid))
448448
.collect::<std::result::Result<Vec<_>, _>>()?;
449449
let parent_refs = parents.iter().collect::<Vec<_>>();
450450
let message = git2::message_prettify(message, None)?;
@@ -455,7 +455,7 @@ impl Git {
455455
// obtained (no user configured), then take the user/email from the
456456
// existing commit but make a new signature which has a timestamp of
457457
// now.
458-
let committer = repo.signature().or_else(|_| {
458+
let committer = self.repo.signature().or_else(|_| {
459459
git2::Signature::now(
460460
String::from_utf8_lossy(
461461
original_commit.committer().name_bytes(),
@@ -478,7 +478,7 @@ impl Git {
478478
.as_ref(),
479479
)?;
480480

481-
let oid = repo.commit(
481+
let oid = self.repo.commit(
482482
None,
483483
&author,
484484
&committer,
@@ -493,7 +493,7 @@ impl Git {
493493
pub fn check_no_uncommitted_changes(&self) -> Result<()> {
494494
let mut opts = git2::StatusOptions::new();
495495
opts.include_ignored(false).include_untracked(false);
496-
if self.repo().statuses(Some(&mut opts))?.is_empty() {
496+
if self.repo.statuses(Some(&mut opts))?.is_empty() {
497497
Ok(())
498498
} else {
499499
Err(Error::new(

src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,9 @@ pub async fn spr() -> Result<()> {
202202
Ok::<_, Error>(())
203203
}
204204

205-
#[tokio::main]
205+
#[tokio::main(flavor = "current_thread")]
206206
async fn main() -> Result<()> {
207-
if let Err(error) = spr().await {
207+
if let Err(error) = tokio::task::LocalSet::new().run_until(spr()).await {
208208
for message in error.messages() {
209209
output("🛑", message)?;
210210
}

0 commit comments

Comments
 (0)