feat(git): rename remote branches atomically via push

Adds a new command to rename remote branches and update tracking refs.
Renaming remote refs is performed atomically using a single push.
The push creates the new remote ref and deletes the old if it succeeds.
The frontend now invokes remote-rename when needed and shows labels.

- Atomic remote rename via push with create/ref and delete
- Frontend supports remote branch renames from the branch panel
- Compare UI now shows labels for remote refs in results
This commit is contained in:
Christoph Brandau
2026-08-13 18:32:16 +02:00
parent ca14fac90c
commit f43fe00873
11 changed files with 447 additions and 71 deletions
+45 -1
View File
@@ -3023,6 +3023,16 @@
.compare-field { display: grid; gap: 4px; min-width: 0; }
.compare-field span { color: var(--color-ink-faint); font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em; }
.compare-arrow { margin-bottom: 6px; color: var(--color-ink-faint); }
.compare-target-help {
margin: 12px;
padding: 10px 12px;
border: 1px solid color-mix(in srgb, var(--color-accent) 20%, var(--color-border-subtle));
border-radius: 8px;
color: var(--color-ink-dim);
background: color-mix(in srgb, var(--color-accent) 5%, var(--color-surface-raised));
font-size: 11.5px;
line-height: 1.5;
}
.compare-summary { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; padding: 10px 12px; }
.compare-range { display: flex; align-items: center; gap: 7px; color: var(--color-ink-muted); }
@@ -3685,6 +3695,34 @@
text-transform: uppercase;
letter-spacing: 0.05em;
}
.new-branch-field > div { display: flex; min-width: 0; }
.new-branch-field > div > input { width: 100%; min-width: 0; }
.remote-branch-name-field > strong {
display: inline-flex;
align-items: center;
flex: 0 0 auto;
height: 34px;
padding: 0 0 0 11px;
border: 1px solid var(--color-border-input);
border-right: 0;
border-radius: var(--ui-radius-sm) 0 0 var(--ui-radius-sm);
color: var(--color-ink-faint);
background: var(--color-surface-dim);
font-family: var(--font-mono);
font-size: 12px;
}
.remote-branch-name-field > input { border-radius: 0 var(--ui-radius-sm) var(--ui-radius-sm) 0; }
.rename-remote-note {
margin: -2px 0 0;
padding: 10px 12px;
border: 1px solid var(--color-border-subtle);
border-radius: 8px;
color: var(--color-ink-dim);
background: var(--color-surface-dim);
font-size: 11.5px;
line-height: 1.5;
}
.rename-remote-note strong { color: var(--color-ink); font-family: var(--font-mono); font-weight: 700; }
.new-branch-actions {
display: flex;
justify-content: flex-end;
@@ -3703,7 +3741,8 @@
.compare-restore { max-width: 170px; min-width: 0; }
.compare-restore span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dialog-range { display: flex; align-items: center; gap: 8px; margin: 2px 0 0; color: var(--color-accent); font-size: 15px; }
.dialog-range { display: flex; align-items: center; gap: 8px; max-width: min(68vw, 780px); margin: 2px 0 0; color: var(--color-accent); font-size: 15px; }
.dialog-range .hash { min-width: 0; max-width: 340px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dialog-title { margin: 2px 0 0; color: var(--color-ink); font-size: 15px; font-weight: 600; }
.dialog-close { min-height: 32px; min-width: 32px; padding: 0; justify-content: center; }
@@ -3852,6 +3891,11 @@
}
.split-col-label + .split-col-label { border-left: 1px solid var(--color-border-subtle); }
.split-col-hash {
min-width: 0;
max-width: min(42%, 260px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 1px 6px;
border-radius: 5px;
background: rgba(90,140,248,0.12);