Skip to content

Commit 14b111e

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 8ffbdab commit 14b111e

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.
@@ -370,30 +386,49 @@ fn submit(
370386
(local, unsubmitted, to_update, to_skip)
371387
});
372388

373-
let (submitted_commit_names, unsubmitted_commit_names): (BTreeSet<String>, BTreeSet<String>) = {
389+
let (submitted_commit_names, unsubmitted_commit_names, create_error_commits): (
390+
BTreeSet<String>,
391+
BTreeSet<String>,
392+
BTreeSet<NonZeroOid>,
393+
) = {
374394
let unsubmitted_commit_names: BTreeSet<String> = unsubmitted_commits
375395
.values()
376396
.flat_map(|commit_status| commit_status.local_commit_name.clone())
377397
.collect();
378398
if create {
379-
let created_commit_names = if dry_run {
380-
unsubmitted_commit_names.clone()
399+
let (created_commit_names, error_commits) = if dry_run {
400+
(unsubmitted_commit_names.clone(), Default::default())
381401
} else {
382402
let create_statuses =
383403
try_exit_code!(forge.create(unsubmitted_commits, &submit_options)?);
384-
create_statuses
385-
.into_values()
386-
.map(
387-
|CreateStatus {
388-
final_commit_oid: _,
389-
local_commit_name,
390-
}| local_commit_name,
391-
)
392-
.collect()
404+
let mut created_commit_names = BTreeSet::new();
405+
let mut error_commits = BTreeSet::new();
406+
for (_commit_oid, create_status) in create_statuses {
407+
match create_status {
408+
CreateStatus::Created {
409+
final_commit_oid: _,
410+
local_commit_name,
411+
} => {
412+
created_commit_names.insert(local_commit_name);
413+
}
414+
// For now, treat `Skipped` the same as `Err` as it would be
415+
// a lot of work to render it differently in the output, and
416+
// we may want to rethink the data structures before doing
417+
// that.
418+
CreateStatus::Skipped { commit_oid } | CreateStatus::Err { commit_oid } => {
419+
error_commits.insert(commit_oid);
420+
}
421+
}
422+
}
423+
(created_commit_names, error_commits)
393424
};
394-
(created_commit_names, Default::default())
425+
(created_commit_names, Default::default(), error_commits)
395426
} else {
396-
(Default::default(), unsubmitted_commit_names)
427+
(
428+
Default::default(),
429+
unsubmitted_commit_names,
430+
Default::default(),
431+
)
397432
}
398433
};
399434

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

520577
#[instrument]

0 commit comments

Comments
 (0)