Skip to content

Commit e7c3653

Browse files
committed
feat(submit:github): implement Github forge
- It's not very good. It's probably buggy since there's a huge amount of state to keep in sync between commits, branches, and pull requests. - It's not very useful. When using Github's "Rebase and merge" button, it always rewrites the commit to update the committer signature. Then, for some reason, descendant commits can't be rebased automatically, and you have to manually rebase them.
1 parent 74f77c1 commit e7c3653

File tree

10 files changed

+1903
-26
lines changed

10 files changed

+1903
-26
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

git-branchless-lib/src/git/reference.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::ffi::OsStr;
33
use std::string::FromUtf8Error;
44

55
use thiserror::Error;
6-
use tracing::instrument;
6+
use tracing::{instrument, warn};
77

88
use crate::git::config::ConfigRead;
99
use crate::git::oid::make_non_zero_oid;
@@ -306,6 +306,37 @@ impl<'repo> Branch<'repo> {
306306
Ok(target_oid)
307307
}
308308

309+
/// If this branch tracks a remote ("upstream") branch, return the name of
310+
/// that branch without the leading remote name. For example, if the
311+
/// upstream branch is `origin/main`, this will return `main`. (Usually,
312+
/// this is the same as the name of the local branch, but not always.)
313+
pub fn get_upstream_branch_name_without_push_remote_name(
314+
&self,
315+
) -> eyre::Result<Option<String>> {
316+
let push_remote_name = match self.get_push_remote_name()? {
317+
Some(stack_remote_name) => stack_remote_name,
318+
None => return Ok(None),
319+
};
320+
let upstream_branch = match self.get_upstream_branch()? {
321+
Some(upstream_branch) => upstream_branch,
322+
None => return Ok(None),
323+
};
324+
let upstream_branch_name = upstream_branch.get_name()?;
325+
let upstream_branch_name_without_remote =
326+
match upstream_branch_name.strip_prefix(&format!("{push_remote_name}/")) {
327+
Some(upstream_branch_name_without_remote) => upstream_branch_name_without_remote,
328+
None => {
329+
warn!(
330+
?push_remote_name,
331+
?upstream_branch,
332+
"Upstream branch name did not start with push remote name"
333+
);
334+
upstream_branch_name
335+
}
336+
};
337+
Ok(Some(upstream_branch_name_without_remote.to_owned()))
338+
}
339+
309340
/// Get the associated remote to push to for this branch. If there is no
310341
/// associated remote, returns `None`. Note that this never reads the value
311342
/// of `push.remoteDefault`.

git-branchless-lib/src/git/repo.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,31 @@ impl Repo {
10351035
Ok((snapshot, statuses))
10361036
}
10371037

1038+
/// Create a new branch or update an existing one. The provided name should
1039+
/// be a branch name and not a reference name, i.e. it should not start with
1040+
/// `refs/heads/`.
1041+
#[instrument]
1042+
pub fn create_branch(&self, branch_name: &str, commit: &Commit, force: bool) -> Result<Branch> {
1043+
if branch_name.starts_with("refs/heads/") {
1044+
warn!(
1045+
?branch_name,
1046+
"Branch name starts with refs/heads/; this is probably not what you intended."
1047+
);
1048+
}
1049+
1050+
let branch = self
1051+
.inner
1052+
.branch(branch_name, &commit.inner, force)
1053+
.map_err(|err| Error::CreateBranch {
1054+
source: err,
1055+
name: branch_name.to_owned(),
1056+
})?;
1057+
Ok(Branch {
1058+
repo: self,
1059+
inner: branch,
1060+
})
1061+
}
1062+
10381063
/// Create a new reference or update an existing one.
10391064
#[instrument]
10401065
pub fn create_reference(

git-branchless-lib/src/git/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub fn make_test_command_slug(command: String) -> String {
4646
/// A version of `NonZeroOid` that can be serialized and deserialized. This
4747
/// exists in case we want to move this type (back) into a separate module which
4848
/// has a `serde` dependency in the interest of improving build times.
49-
#[derive(Debug)]
49+
#[derive(Clone, Debug, Eq, PartialEq)]
5050
pub struct SerializedNonZeroOid(pub NonZeroOid);
5151

5252
impl Serialize for SerializedNonZeroOid {

git-branchless-lib/src/testing.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ then you can only run tests in the main `git-branchless` and \
533533
/// Commit a file with given contents and message. The `time` argument is
534534
/// used to set the commit timestamp, which is factored into the commit
535535
/// hash. The filename is always appended to the message prefix.
536+
#[track_caller]
536537
#[instrument]
537538
pub fn commit_file_with_contents_and_message(
538539
&self,
@@ -563,6 +564,7 @@ then you can only run tests in the main `git-branchless` and \
563564
/// Commit a file with given contents and a default message. The `time`
564565
/// argument is used to set the commit timestamp, which is factored into the
565566
/// commit hash.
567+
#[track_caller]
566568
#[instrument]
567569
pub fn commit_file_with_contents(
568570
&self,
@@ -575,6 +577,8 @@ then you can only run tests in the main `git-branchless` and \
575577

576578
/// Commit a file with default contents. The `time` argument is used to set
577579
/// the commit timestamp, which is factored into the commit hash.
580+
#[track_caller]
581+
#[instrument]
578582
pub fn commit_file(&self, name: &str, time: isize) -> eyre::Result<NonZeroOid> {
579583
self.commit_file_with_contents(name, time, &format!("{name} contents\n"))
580584
}

git-branchless-submit/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ git-branchless-invoke = { workspace = true }
1616
git-branchless-opts = { workspace = true }
1717
git-branchless-revset = { workspace = true }
1818
git-branchless-test = { workspace = true }
19+
indexmap = { workspace = true }
1920
itertools = { workspace = true }
2021
lazy_static = { workspace = true }
2122
lib = { workspace = true }
2223
rayon = { workspace = true }
2324
regex = { workspace = true }
2425
serde = { workspace = true, features = ["derive"] }
2526
serde_json = { workspace = true }
27+
tempfile = { workspace = true }
2628
thiserror = { workspace = true }
2729
tracing = { workspace = true }
2830

git-branchless-submit/src/branch_forge.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use lib::git::{
1313
};
1414
use lib::try_exit_code;
1515
use lib::util::{ExitCode, EyreExitOr};
16-
use tracing::warn;
16+
use tracing::{instrument, warn};
1717

1818
use crate::{CommitStatus, CreateStatus, Forge, SubmitOptions, SubmitStatus};
1919

@@ -28,6 +28,7 @@ pub struct BranchForge<'a> {
2828
}
2929

3030
impl Forge for BranchForge<'_> {
31+
#[instrument]
3132
fn query_status(
3233
&mut self,
3334
commit_set: CommitSet,
@@ -185,6 +186,7 @@ impl Forge for BranchForge<'_> {
185186
Ok(Ok(commit_statuses))
186187
}
187188

189+
#[instrument]
188190
fn create(
189191
&mut self,
190192
commits: HashMap<NonZeroOid, CommitStatus>,
@@ -204,6 +206,10 @@ impl Forge for BranchForge<'_> {
204206
.sorted()
205207
.collect_vec();
206208

209+
// FIXME: in principle, it's possible for a branch to have been assigned
210+
// a remote without having been pushed and having created a
211+
// corresponding remote branch. In those cases, we should use the
212+
// branch's associated remote.
207213
let push_remote: String = match self.repo.get_default_push_remote()? {
208214
Some(push_remote) => push_remote,
209215
None => {
@@ -258,6 +264,7 @@ These remotes are available: {}",
258264
}
259265
}
260266

267+
#[instrument]
261268
fn update(
262269
&mut self,
263270
commits: HashMap<NonZeroOid, CommitStatus>,
@@ -275,7 +282,7 @@ These remotes are available: {}",
275282
commit_status => {
276283
warn!(
277284
?commit_status,
278-
"Commit was requested to be updated, but it did not have the requisite information."
285+
"Commit was requested to be updated, but it did not have the requisite information (remote name, local branch name)."
279286
);
280287
None
281288
}

0 commit comments

Comments
 (0)