Support deleting remote branches via the delete button

The delete button and context menu action now detect whether the
selected branch is remote (tracked via Qt::UserRole on list items).
Remote branches are deleted with `git push --delete <remote> <branch>`
with an appropriate confirmation dialog warning about server-side
removal. Local branches continue using `git branch -D`.
This commit is contained in:
Levi Neely 2026-07-16 05:38:10 +02:00
parent 3642f8b743
commit 7dabd4b9e6
2 changed files with 66 additions and 20 deletions

View File

@ -836,6 +836,7 @@ void GitPlusPlusView::onBranchFinished(int exitCode, QProcess::ExitStatus)
if (exitCode == 0) {
QByteArray output = proc->readAllStandardOutput();
m_allBranches.clear();
m_remoteBranches.clear();
QString current;
const auto lines = output.split('\n');
@ -849,17 +850,25 @@ void GitPlusPlusView::onBranchFinished(int exitCode, QProcess::ExitStatus)
// Skip HEAD pointer in remotes
if (branch.contains(QStringLiteral(" -> "))) continue;
// Strip "remotes/" prefix for display (show as origin/foo)
if (branch.startsWith(QStringLiteral("remotes/")))
bool isRemote = false;
if (branch.startsWith(QStringLiteral("remotes/"))) {
branch = branch.mid(8); // len("remotes/") == 8
isRemote = true;
}
// Deduplicate: if local branch matches a remote, skip the remote
if (!m_allBranches.contains(branch))
if (!m_allBranches.contains(branch)) {
m_allBranches.append(branch);
if (isRemote)
m_remoteBranches.append(branch);
}
}
// Update branch list widget
m_branchList->clear();
for (const auto &b : m_allBranches) {
auto *item = new QListWidgetItem(b, m_branchList);
// Mark remote branches (those that came from remotes/ prefix)
item->setData(Qt::UserRole, m_remoteBranches.contains(b));
if (b == current) {
item->setText(QStringLiteral("* ") + b);
QFont f = item->font();
@ -1202,26 +1211,62 @@ void GitPlusPlusView::deleteBranch()
return;
}
if (QMessageBox::question(m_toolView, QStringLiteral("Delete Branch"),
QStringLiteral("Delete branch '%1'?").arg(branch),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes)
return;
// Detect if this is a remote branch via the UserRole flag
auto *item = m_branchList->currentItem();
bool isRemote = item && item->data(Qt::UserRole).toBool();
// Use -D for force delete (handles unmerged branches)
auto *proc = gitProcess({QStringLiteral("branch"), QStringLiteral("-D"), branch});
if (!proc) return;
connect(proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, [this, proc, branch](int exitCode, QProcess::ExitStatus) {
if (exitCode == 0) {
setMessage(QStringLiteral("Deleted branch: ") + branch);
} else {
QString err = QString::fromUtf8(proc->readAllStandardError()).trimmed();
setMessage(QStringLiteral("Delete failed: ") + err, true);
if (isRemote) {
// Remote branch: extract remote name and branch name (e.g. "origin/feature" -> "origin", "feature")
int sep = branch.indexOf(QLatin1Char('/'));
if (sep <= 0) {
setMessage(QStringLiteral("Cannot determine remote for branch: ") + branch, true);
return;
}
proc->deleteLater();
refreshAll();
});
proc->start();
QString remote = branch.left(sep);
QString remoteBranch = branch.mid(sep + 1);
if (QMessageBox::question(m_toolView, QStringLiteral("Delete Remote Branch"),
QStringLiteral("Delete remote branch '%1' from '%2'?\n\nThis will remove it from the remote server.").arg(remoteBranch, remote),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes)
return;
auto *proc = gitProcess({QStringLiteral("push"), QStringLiteral("--delete"), remote, remoteBranch});
if (!proc) return;
connect(proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, [this, proc, branch](int exitCode, QProcess::ExitStatus) {
if (exitCode == 0) {
setMessage(QStringLiteral("Deleted remote branch: ") + branch);
} else {
QString err = QString::fromUtf8(proc->readAllStandardError()).trimmed();
setMessage(QStringLiteral("Delete failed: ") + err, true);
}
proc->deleteLater();
refreshAll();
});
proc->start();
} else {
// Local branch
if (QMessageBox::question(m_toolView, QStringLiteral("Delete Branch"),
QStringLiteral("Delete local branch '%1'?").arg(branch),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes)
return;
// Use -D for force delete (handles unmerged branches)
auto *proc = gitProcess({QStringLiteral("branch"), QStringLiteral("-D"), branch});
if (!proc) return;
connect(proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, [this, proc, branch](int exitCode, QProcess::ExitStatus) {
if (exitCode == 0) {
setMessage(QStringLiteral("Deleted branch: ") + branch);
} else {
QString err = QString::fromUtf8(proc->readAllStandardError()).trimmed();
setMessage(QStringLiteral("Delete failed: ") + err, true);
}
proc->deleteLater();
refreshAll();
});
proc->start();
}
}
void GitPlusPlusView::compareBranch()

View File

@ -173,6 +173,7 @@ private:
QString m_gitDir;
QString m_currentBranch;
QStringList m_allBranches;
QStringList m_remoteBranches;
bool m_isDirty = false;
QPointer<QProcess> m_remoteProc;
int m_spinnerFrame = 0;