Skip to content

Commit 5be084c

Browse files
committed
feat(submit:phabricator): do not abort entire process on failure
Currently, `git submit` for Phabricator will abort the entire operation if any commit fails to be submitted. This means that if `arc diff` succeeds on one commit and then fails on its child, the entire operation is aborted. However, the first `arc diff` had side effects, so the user gets diffs uploaded to Phabricator that are not reflected locally. Instead, we should confirm any passing commits and abort after we get a failing commit. This commit updates the Phabricator forge to handle the error case better and not produce garbage commits on Phabricator.
1 parent 4ad07c3 commit 5be084c

File tree

7 files changed

+280
-89
lines changed

7 files changed

+280
-89
lines changed

git-branchless-lib/src/core/eventlog.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,6 @@ INSERT INTO event_log VALUES (
580580
///
581581
/// Returns: All the events in the database, ordered from oldest to newest.
582582
#[instrument]
583-
584583
pub fn get_events(&self) -> eyre::Result<Vec<Event>> {
585584
let mut stmt = self.conn.prepare(
586585
"

git-branchless-submit/src/branch_forge.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ These remotes are available: {}",
253253
commit_status.local_commit_name.map(|local_commit_name| {
254254
(
255255
commit_oid,
256-
CreateStatus {
256+
CreateStatus::Created {
257257
final_commit_oid: commit_oid,
258258
local_commit_name,
259259
},

git-branchless-submit/src/github.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ impl Forge for GithubForge<'_> {
298298
.copied()
299299
.map(|(commit_oid, commit_status)| {
300300
let commit_status = match created_branches.get(&commit_oid) {
301-
Some(CreateStatus {
301+
Some(CreateStatus::Created {
302302
final_commit_oid: _,
303303
local_commit_name,
304304
}) => CommitStatus {
@@ -309,7 +309,9 @@ impl Forge for GithubForge<'_> {
309309
// Expecting this to be the same as the local branch name (for now):
310310
remote_commit_name: Some(local_commit_name.clone()),
311311
},
312-
None => commit_status.clone(),
312+
Some(CreateStatus::Skipped { .. } | CreateStatus::Err { .. }) | None => {
313+
commit_status.clone()
314+
}
313315
};
314316
(commit_oid, commit_status)
315317
})

git-branchless-submit/src/lib.rs

Lines changed: 85 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -141,18 +141,34 @@ pub struct SubmitOptions {
141141

142142
/// The result of creating a commit.
143143
#[derive(Clone, Debug)]
144-
pub struct CreateStatus {
145-
/// The commit OID after carrying out the creation process. Usually, this
146-
/// will be the same as the original commit OID, unless the forge amends it
147-
/// (e.g. to include a change ID).
148-
pub final_commit_oid: NonZeroOid,
149-
150-
/// An identifier corresponding to the commit, for display purposes only.
151-
/// This may be a branch name, a change ID, the commit summary, etc.
152-
///
153-
/// This does not necessarily correspond to the commit's name/identifier in
154-
/// the forge (e.g. not a code review link).
155-
pub local_commit_name: String,
144+
pub enum CreateStatus {
145+
/// The commit was successfully created.
146+
Created {
147+
/// The commit OID after carrying out the creation process. Usually, this
148+
/// will be the same as the original commit OID, unless the forge amends it
149+
/// (e.g. to include a change ID).
150+
final_commit_oid: NonZeroOid,
151+
152+
/// An identifier corresponding to the commit, for display purposes only.
153+
/// This may be a branch name, a change ID, the commit summary, etc.
154+
///
155+
/// This does not necessarily correspond to the commit's name/identifier in
156+
/// the forge (e.g. not a code review link).
157+
local_commit_name: String,
158+
},
159+
160+
/// The commit was skipped. This could happen if a previous commit could not
161+
/// be created due to an error.
162+
Skipped {
163+
/// The commit which was skipped.
164+
commit_oid: NonZeroOid,
165+
},
166+
167+
/// The commit could not be created due to an error.
168+
Err {
169+
/// The commit which produced an error.
170+
commit_oid: NonZeroOid,
171+
},
156172
}
157173

158174
/// "Forge" refers to a Git hosting provider, such as GitHub, GitLab, etc.
@@ -367,30 +383,49 @@ fn submit(
367383
(local, unsubmitted, to_update, to_skip)
368384
});
369385

370-
let (submitted_commit_names, unsubmitted_commit_names): (BTreeSet<String>, BTreeSet<String>) = {
386+
let (submitted_commit_names, unsubmitted_commit_names, create_error_commits): (
387+
BTreeSet<String>,
388+
BTreeSet<String>,
389+
BTreeSet<NonZeroOid>,
390+
) = {
371391
let unsubmitted_commit_names: BTreeSet<String> = unsubmitted_commits
372392
.values()
373393
.flat_map(|commit_status| commit_status.local_commit_name.clone())
374394
.collect();
375395
if create {
376-
let created_commit_names = if dry_run {
377-
unsubmitted_commit_names.clone()
396+
let (created_commit_names, error_commits) = if dry_run {
397+
(unsubmitted_commit_names.clone(), Default::default())
378398
} else {
379399
let create_statuses =
380400
try_exit_code!(forge.create(unsubmitted_commits, &submit_options)?);
381-
create_statuses
382-
.into_values()
383-
.map(
384-
|CreateStatus {
385-
final_commit_oid: _,
386-
local_commit_name,
387-
}| local_commit_name,
388-
)
389-
.collect()
401+
let mut created_commit_names = BTreeSet::new();
402+
let mut error_commits = BTreeSet::new();
403+
for (_commit_oid, create_status) in create_statuses {
404+
match create_status {
405+
CreateStatus::Created {
406+
final_commit_oid: _,
407+
local_commit_name,
408+
} => {
409+
created_commit_names.insert(local_commit_name);
410+
}
411+
// For now, treat `Skipped` the same as `Err` as it would be
412+
// a lot of work to render it differently in the output, and
413+
// we may want to rethink the data structures before doing
414+
// that.
415+
CreateStatus::Skipped { commit_oid } | CreateStatus::Err { commit_oid } => {
416+
error_commits.insert(commit_oid);
417+
}
418+
}
419+
}
420+
(created_commit_names, error_commits)
390421
};
391-
(created_commit_names, Default::default())
422+
(created_commit_names, Default::default(), error_commits)
392423
} else {
393-
(Default::default(), unsubmitted_commit_names)
424+
(
425+
Default::default(),
426+
unsubmitted_commit_names,
427+
Default::default(),
428+
)
394429
}
395430
};
396431

@@ -510,8 +545,30 @@ repository. To submit them, retry this operation with the --create option.",
510545
if dry_run { "are" } else { "were" },
511546
)?;
512547
}
513-
514-
Ok(Ok(()))
548+
if !create_error_commits.is_empty() {
549+
writeln!(
550+
effects.get_output_stream(),
551+
"Failed to create {}:",
552+
Pluralize {
553+
determiner: None,
554+
amount: create_error_commits.len(),
555+
unit: ("commit", "commits")
556+
},
557+
)?;
558+
for error_commit_oid in &create_error_commits {
559+
let error_commit = repo.find_commit_or_fail(*error_commit_oid)?;
560+
writeln!(
561+
effects.get_output_stream(),
562+
"{}",
563+
effects
564+
.get_glyphs()
565+
.render(error_commit.friendly_describe(effects.get_glyphs())?)?
566+
)?;
567+
}
568+
Ok(Err(ExitCode(1)))
569+
} else {
570+
Ok(Ok(()))
571+
}
515572
}
516573

517574
#[instrument]

0 commit comments

Comments
 (0)