feat(commit,tag): 推送环节感知远程配置并支持多远程选择推送

- 仓库未配置任何远程时跳过推送询问并打印说明(--push 显式指定同样跳过)
- --remote 指定的目标不存在时回退到第一个可用远程(字母序)并明确提示
- 新增 --select-remote:多远程仓库弹出多选列表依次推送,失败即中止并汇总已推送/未尝试
- 优先级链:--yes > --select-remote > --push
- commit/tag 推送段收敛为 commands::run_push_flow 共享流程
- GitRepo 新增 list_remotes/resolve_push_target,i18n 新文案覆盖 7 种语言
This commit is contained in:
2026-08-31 14:19:58 +08:00
parent b391bdabaf
commit fac96021fb
8 changed files with 939 additions and 41 deletions

View File

@@ -413,6 +413,114 @@ impl Messages {
}
}
pub fn push_skipped_no_remote(&self) -> &str {
match self.language {
Language::English => "No remote configured, skipping push.",
Language::Chinese => "未检测到远程仓库,跳过推送。",
Language::Japanese => "リモートが設定されていないため、プッシュをスキップします。",
Language::Korean => "원격이 구성되지 않아 푸시를 건너뜁니다.",
Language::Spanish => "No hay remoto configurado, se omite el envío.",
Language::French => "Aucun distant configuré, envoi ignoré.",
Language::German => "Kein Remote konfiguriert, Push wird übersprungen.",
}
}
pub fn push_target_fallback(&self, requested: &str, actual: &str) -> String {
match self.language {
Language::English => format!(
"Remote '{}' not found, pushing to '{}' instead.",
requested, actual
),
Language::Chinese => format!("未找到远程 '{}',将推送到 '{}'。", requested, actual),
Language::Japanese => {
format!("リモート '{}' が見つからないため、'{}' にプッシュします。", requested, actual)
}
Language::Korean => format!(
"'{}' 원격을 찾을 수 없어 '{}'로 푸시합니다.",
requested, actual
),
Language::Spanish => format!(
"Remoto '{}' no encontrado, se enviará a '{}' en su lugar.",
requested, actual
),
Language::French => format!(
"Distant '{}' introuvable, envoi vers '{}' à la place.",
requested, actual
),
Language::German => format!(
"Remote '{}' nicht gefunden, stattdessen Push zu '{}'.",
requested, actual
),
}
}
pub fn select_push_remotes(&self) -> &str {
match self.language {
Language::English => {
"Select remote(s) to push to (space to toggle, enter to confirm):"
}
Language::Chinese => "选择要推送的远程仓库(空格勾选,回车确认):",
Language::Japanese => {
"プッシュ先のリモートを選択してくださいスペースで選択、Enterで確定"
}
Language::Korean => "푸시할 원격을 선택하세요(스페이스로 선택, Enter로 확정):",
Language::Spanish => {
"Selecciona los remotos donde enviar (espacio para marcar, enter para confirmar):"
}
Language::French => {
"Sélectionnez les distants où envoyer (espace pour cocher, entrée pour valider) :"
}
Language::German => {
"Zu pushende Remotes auswählen (Leertaste zum Markieren, Enter zum Bestätigen):"
}
}
}
pub fn no_remotes_selected(&self) -> &str {
match self.language {
Language::English => "No remote selected, skipping push.",
Language::Chinese => "未选择任何远程,跳过推送。",
Language::Japanese => "リモートが選択されていないため、プッシュをスキップします。",
Language::Korean => "선택된 원격이 없어 푸시를 건너뜁니다.",
Language::Spanish => "No se seleccionó ningún remoto, se omite el envío.",
Language::French => "Aucun distant sélectionné, envoi ignoré.",
Language::German => "Kein Remote ausgewählt, Push wird übersprungen.",
}
}
pub fn push_multi_failed(&self, failed: &str, pushed: &str, pending: &str) -> String {
match self.language {
Language::English => format!(
"Push to '{}' failed. Pushed: {}. Not attempted: {}.",
failed, pushed, pending
),
Language::Chinese => format!(
"推送到 '{}' 失败。已推送:{}。未尝试:{}",
failed, pushed, pending
),
Language::Japanese => format!(
"'{}' へのプッシュに失敗しました。プッシュ済み: {}。未実行: {}",
failed, pushed, pending
),
Language::Korean => format!(
"'{}' 푸시에 실패했습니다. 푸시됨: {}. 시도하지 않음: {}.",
failed, pushed, pending
),
Language::Spanish => format!(
"Error al enviar a '{}'. Enviados: {}. Sin intentar: {}.",
failed, pushed, pending
),
Language::French => format!(
"Échec de l'envoi vers '{}'. Envoyés : {}. Non tentés : {}.",
failed, pushed, pending
),
Language::German => format!(
"Push zu '{}' fehlgeschlagen. Gepusht: {}. Nicht versucht: {}.",
failed, pushed, pending
),
}
}
pub fn ai_analyzing(&self) -> &str {
match self.language {
Language::English => "AI is analyzing your changes...",
@@ -4875,3 +4983,19 @@ impl Messages {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_multi_failed_lists_failed_pushed_and_pending() {
let messages = Messages::new(Language::English);
let msg = messages.push_multi_failed("origin", "upstream, gitlab", "-");
assert!(msg.contains("Push to 'origin' failed."), "got: {}", msg);
assert!(msg.contains("Pushed: upstream, gitlab."), "got: {}", msg);
assert!(msg.contains("Not attempted: -."), "got: {}", msg);
}
}