Skip to content

Commit 6fcb614

Browse files
committed
refactor: extract functions from git_high_level module
1 parent a113a3d commit 6fcb614

File tree

4 files changed

+157
-175
lines changed

4 files changed

+157
-175
lines changed

src/commands/run.rs

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! `run` subcommand
22
3-
use crate::config::{self, BranchName, Config, PullRequest};
3+
use crate::config::{self, BranchName, Config, PrNumber, PullRequest};
4+
use anyhow::Result;
45
use std::ffi::OsString;
56
use std::fs::{self, File};
67
use std::io::Write as _;
@@ -9,7 +10,6 @@ use std::path::PathBuf;
910
use anyhow::{anyhow, bail};
1011
use colored::Colorize as _;
1112

12-
use crate::git_high_level;
1313
use crate::github::{self, Branch, Remote, RemoteBranch};
1414
use crate::utils::{format_pr, format_url, with_uuid};
1515
use crate::{commands, confirm_prompt, git};
@@ -23,7 +23,7 @@ struct FileBackup {
2323
}
2424

2525
/// Run patchy, if `yes` then there will be no prompt
26-
pub async fn run(yes: bool, use_gh_cli: bool) -> anyhow::Result<()> {
26+
pub async fn run(yes: bool, use_gh_cli: bool) -> Result<()> {
2727
let root = config::ROOT.as_str();
2828

2929
let Ok(config_string) = fs::read_to_string(&*config::FILE_PATH) else {
@@ -114,7 +114,7 @@ pub async fn run(yes: bool, use_gh_cli: bool) -> anyhow::Result<()> {
114114
},
115115
};
116116

117-
git_high_level::add_remote_branch(&info, commit.as_ref())?;
117+
github::add_remote_branch(&info, commit.as_ref())?;
118118

119119
// we want to checkout the `branch` of `remote`
120120
let branch = &info.branch.local_branch_name;
@@ -171,12 +171,9 @@ pub async fn run(yes: bool, use_gh_cli: bool) -> anyhow::Result<()> {
171171
continue;
172172
};
173173

174-
if let Err(err) = git_high_level::merge_pull_request(
175-
&info,
176-
*pull_request,
177-
&response.title,
178-
&response.html_url,
179-
) {
174+
if let Err(err) =
175+
merge_pull_request(&info, *pull_request, &response.title, &response.html_url)
176+
{
180177
log::error!("failed to merge {pull_request}: {err}");
181178
continue;
182179
}
@@ -201,7 +198,7 @@ pub async fn run(yes: bool, use_gh_cli: bool) -> anyhow::Result<()> {
201198
continue;
202199
};
203200

204-
if let Err(err) = git_high_level::merge(
201+
if let Err(err) = merge(
205202
&info.branch.local_branch_name,
206203
&info.branch.upstream_branch_name,
207204
) {
@@ -327,3 +324,66 @@ pub async fn run(yes: bool, use_gh_cli: bool) -> anyhow::Result<()> {
327324

328325
Ok(())
329326
}
327+
328+
/// Create a merge commit that merges the `other_branch` into `current_branch`
329+
pub fn merge(
330+
current_branch: &BranchName,
331+
other_branch: &BranchName,
332+
) -> Result<String, anyhow::Error> {
333+
log::trace!("Merging branch {current_branch}");
334+
335+
if let Err(err) = git::merge(current_branch.as_ref()) {
336+
git::nuke_worktree()?;
337+
bail!("failed to merge {other_branch}\n{err}");
338+
}
339+
340+
// --squash will NOT commit anything. So we need to make the commit it manually
341+
git::commit(&format!("Merge {current_branch}"))?;
342+
343+
Ok(format!("Merged {other_branch} successfully"))
344+
}
345+
346+
/// Merge the `pull_request` into patchy's branch
347+
pub fn merge_pull_request(
348+
info: &RemoteBranch,
349+
pull_request: PrNumber,
350+
pr_title: &str,
351+
pr_url: &str,
352+
) -> Result<()> {
353+
merge(
354+
&info.branch.local_branch_name,
355+
&info.branch.upstream_branch_name,
356+
)
357+
.map_err(|err| {
358+
let pr = format_pr(pull_request, pr_title, pr_url);
359+
360+
let support_url = format_url(
361+
"Merge conflicts (github)",
362+
"https://github.com/nik-rev/patchy?tab=readme-ov-file#merge-conflicts",
363+
)
364+
.bright_blue();
365+
366+
anyhow!(
367+
"Could not merge branch {} into the current branch for pull request {pr} since the \
368+
merge is non-trivial.\nYou will need to merge it yourself:\n {} {0}\nNote: To learn \
369+
how to merge only once and re-use for subsequent invocations of patchy, see \
370+
{support_url}\nSkipping this PR. Error message from git:\n{err}",
371+
&info.branch.local_branch_name.as_ref().bright_cyan(),
372+
"git merge --squash".bright_blue()
373+
)
374+
})?;
375+
376+
if git::is_worktree_dirty() {
377+
git::commit(&format!(
378+
"auto-merge pull request {}",
379+
&pr_url.replace("github.com", "redirect.github.com")
380+
))?;
381+
}
382+
383+
git::delete_remote_and_branch(
384+
&info.remote.local_remote_alias,
385+
&info.branch.local_branch_name,
386+
)?;
387+
388+
Ok(())
389+
}

src/git_high_level.rs

Lines changed: 0 additions & 161 deletions
This file was deleted.

src/github.rs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use tap::Pipe as _;
77

88
use crate::{
99
config::{BranchName, CommitId, PrNumber, RepoName, RepoOwner},
10-
git_high_level::{AvailableBranch, add_remote_branch, find_first_available_branch},
10+
git,
1111
utils::{make_request, normalize_commit_msg, with_uuid},
1212
};
13-
use anyhow::{Result, anyhow};
13+
use anyhow::{Result, anyhow, bail};
1414

1515
/// Data returned by GitHub's API for the pull request endpoint per repo
1616
#[derive(Serialize, Deserialize, Debug)]
@@ -190,3 +190,87 @@ pub async fn fetch_pull_request(
190190

191191
Ok((response, remote_branch))
192192
}
193+
194+
/// Available branch name to use
195+
pub enum AvailableBranch {
196+
/// In this case, we can just use the original `branch` that we passed in
197+
First,
198+
/// The first branch was available, so we slapped on some arbitrary
199+
/// identifier at the end Represents a branch like some-branch-2,
200+
/// some-branch-3
201+
Other(BranchName),
202+
}
203+
204+
/// Given a branch, either return this branch or the first available branch with
205+
/// an identifier at the end (a `-#`) where `#` represents a number
206+
/// So we can keep on "trying" for a branch that isn't used. We might try
207+
/// `some-branch`, and if it already exists we will then try:
208+
///
209+
/// - some-branch-2
210+
/// - some-branch-3
211+
/// - some-branch-4
212+
/// - ...
213+
///
214+
/// Stopping when we find the first available
215+
///
216+
/// We do not want to return a branch if it already exists, since we don't want
217+
/// to overwrite any branch potentially losing the user their work
218+
///
219+
/// We also don't want to ask for a prompt for a custom name, as it would be
220+
/// pretty annoying to specify a name for each branch if you have like 30 pull
221+
/// requests you want to merge
222+
pub fn find_first_available_branch(branch: &str) -> AvailableBranch {
223+
if git::does_object_exist(branch) {
224+
return AvailableBranch::First;
225+
}
226+
227+
// the first number for which the branch does not exist
228+
#[expect(
229+
clippy::maybe_infinite_iter,
230+
reason = "there is definitely not an infinite number of branches"
231+
)]
232+
let number = (2..)
233+
.find(|current| git::does_object_exist(&format!("{current}-{branch}")))
234+
.expect("There will eventually be a #-branch which is available.");
235+
236+
let branch_name = BranchName::try_new(format!("{number}-{branch}"))
237+
.expect("existing git branch is a valid branch name");
238+
239+
AvailableBranch::Other(branch_name)
240+
}
241+
242+
/// Fetches a branch of a remote into local. Optionally accepts a commit hash
243+
/// for versioning.
244+
pub fn add_remote_branch(remote_branch: &RemoteBranch, commit: Option<&CommitId>) -> Result<()> {
245+
git::add_remote(
246+
&remote_branch.remote.local_remote_alias,
247+
&remote_branch.remote.repository_url,
248+
)
249+
.map_err(|err| anyhow!("failed to fetch remote: {err}"))?;
250+
251+
if let Err(err) = git::fetch_remote_branch(
252+
&remote_branch.branch.local_branch_name,
253+
&remote_branch.branch.upstream_branch_name,
254+
&remote_branch.remote.repository_url,
255+
) {
256+
bail!(
257+
"Failed to find branch {} of GitHub repository {}. Are you sure it exists?\n{err}",
258+
remote_branch.branch.upstream_branch_name,
259+
remote_branch.remote.repository_url
260+
);
261+
}
262+
263+
if let Some(commit) = commit {
264+
git::reset_branch_to_commit(&remote_branch.branch.local_branch_name, commit).map_err(
265+
|err| {
266+
anyhow!(
267+
"Failed to find commit {} of branch {}. Are you sure the commit exists?\n{err}",
268+
commit.as_ref(),
269+
remote_branch.branch.local_branch_name
270+
)
271+
},
272+
)?;
273+
}
274+
275+
Ok(())
276+
}

src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ mod cli;
66
mod commands;
77
mod config;
88
mod git;
9-
mod git_high_level;
109
mod github;
1110
mod utils;
1211

0 commit comments

Comments
 (0)