Zum Inhalt

Git-Konflikt lösen

Ziel

Nach einem abgelehnten Push oder einem Konflikt beim Pull sind deine Commits ohne Datenverlust auf Codeberg, und der Server passt wieder zum Repo.

Voraussetzungen

Nie git push --force auf main

Ein Force-Push überschreibt die Commits der anderen auf Codeberg. Ihre Änderungen sind danach aus dem Repo verschwunden, laufen aber weiter auf dem Server. Schlägt ein normaler git push fehl, geh nach dieser Anleitung vor.

Schritte

Du kommst hierher, weil git push mit rejected abgelehnt wurde oder weil git pull --rebase einen Konflikt gemeldet hat. Ein abgelehnter Push sieht so aus:

Text Only
1
2
3
4
5
6
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'ssh://codeberg.org/garagelab/infra.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.

1. Neuesten Stand holen

Im Repo-Ordner:

Bash
git pull --rebase

Erwartet, wenn die anderen andere Stellen geändert haben als du:

Text Only
1
2
3
From ssh://codeberg.org/garagelab/infra
   b3ffca9..671dedc  main       -> origin/main
Successfully rebased and updated refs/heads/main.

Notiere dir die beiden Commit-Kennungen aus der Zeile mit -> (hier b3ffca9..671dedc); du brauchst sie in Schritt 5. Weiter mit Schritt 5.

Haben die anderen dieselben Zeilen geändert wie du, meldet Git einen Konflikt und hält mitten im Rebase an:

Text Only
1
2
3
4
5
6
7
From ssh://codeberg.org/garagelab/infra
   a213caf..585b4dd  main       -> origin/main
Auto-merging syslet/werner/stack-hedgedoc.cue
CONFLICT (content): Merge conflict in syslet/werner/stack-hedgedoc.cue
error: could not apply fa5cd6c... update hedgedoc to 1.13.0
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".

Dann weiter mit Schritt 2.

Meldet Git stattdessen error: cannot pull with rebase: You have unstaged changes., hast du noch Änderungen, die nicht committet sind. Roll sie aus und committe sie nach Änderung ausrollen oder verwirf sie mit git restore <datei>, dann fang hier noch einmal an.

2. Konflikt finden

Bash
git status

Erwartet: You are currently rebasing branch 'main' und unter Unmerged paths: die Dateien mit Konflikt, etwa:

Text Only
1
2
3
4
Unmerged paths:
  (use "git restore --staged <file>..." to unstage)
  (use "git add <file>..." to mark resolution)
        both modified:   syslet/werner/stack-hedgedoc.cue

Steht dort eine Datei terraform.tfstate, löse den Konflikt nicht selbst. Brich mit git rebase --abort ab (Erwartet: git status zeigt wieder On branch main) und frag in Signal nach, wer gerade Terraform ausgeführt hat; was der State ist, steht in Terraform.

Bist du an irgendeiner Stelle unsicher, gilt dasselbe: git rebase --abort setzt dein Repo auf den Stand vor dem Pull zurück, ohne etwas zu verlieren. Frag dann in Signal nach, bevor du weitermachst.

3. Konflikt auflösen

Öffne jede Datei unter Unmerged paths: in deinem Editor. Git hat an der Konfliktstelle beide Fassungen eingefügt, getrennt durch Konfliktmarker:

Text Only
1
2
3
4
5
<<<<<<< HEAD
image: hedgedoc: tag: "1.12.2"
=======
image: hedgedoc: tag: "1.13.0"
>>>>>>> fa5cd6c (update hedgedoc to 1.13.0)
  • zwischen <<<<<<< HEAD und =======: der Stand auf Codeberg, also die Änderung der anderen
  • zwischen ======= und >>>>>>>: deine Änderung, dahinter dein Commit und seine Message

Ersetze den ganzen Block durch die Fassung, die gelten soll, und lösche alle drei Markerzeilen. Welche Fassung gilt, siehst du mit git log -3 --format='%h %an %ar %s' origin/main -- <datei>: Dort steht, wer die andere Änderung gemacht hat und warum. Ist nicht eindeutig, welche Fassung richtig ist, sprich dich mit dieser Person in Signal ab.

Prüfe danach, dass keine Marker übrig sind, und formatiere die CUE-Dateien im Ordner der Datei:

Bash
git diff --check
cue fmt ./...

Erwartet: beide Befehle geben nichts aus. Meldet git diff --check leftover conflict marker, steht in der Zeile davor die Datei und die Zeilennummer eines vergessenen Markers.

4. Rebase fortsetzen

Nur wenn es in Schritt 2 einen Konflikt gab:

Bash
git add <geänderte Dateien>
git status

Erwartet: all conflicts fixed: run "git rebase --continue".

Bash
git rebase --continue

Es öffnet sich dein Editor mit der Commit-Message deines Commits; lass sie, wie sie ist, speichere und schließe den Editor. Erwartet: Successfully rebased and updated refs/heads/main.

Hast du mehrere Commits noch nicht gepusht, kann Git beim nächsten Commit wieder mit einem Konflikt anhalten; dann wiederhole die Schritte 2 bis 4, bis Successfully rebased erscheint.

5. Prüfen, was dazugekommen ist

Mit den beiden Commit-Kennungen aus Schritt 1:

Bash
git log --stat b3ffca9..671dedc

Erwartet: die Commits der anderen mit den Dateien, die sie geändert haben. Merke dir, welche Ordner betroffen sind (syslet/werner/, syslet/containerhost/, desec/, apps/authentik/…); in diesen und in den Ordnern deiner eigenen Änderung führst du die nächsten Schritte aus.

6. plan erneut ausführen

Hast du deine Änderung schon ausgerollt, lief auf dem Server ein Stand ohne die Commits der anderen. Führe deshalb in jedem Ordner aus Schritt 5 plan aus, wie in Änderung ausrollen, Schritt 2 und 3, für Authentik wie in Terraform für Authentik ausführen:

Bash
cue cmd plan

Erwartet: entweder „No changes“ oder nur Änderungen, die zu deinen Commits oder zu den Commits aus Schritt 5 gehören. Ein Beispiel: Du hast Discourse aktualisiert und ausgerollt, während jemand HedgeDoc von 1.12.0 auf 1.12.1 gehoben hat. Dann zeigt plan in syslet/werner, dass HedgeDoc von old: …:1.12.0 auf new: …:1.12.1 gesetzt wird, weil dein apply die Änderung der anderen Person zurückgedreht hat.

Zeigt plan etwas, das zu keinem dieser Commits passt, roll nicht aus, sondern geh nach plan zeigt unerwartete Änderungen vor.

Hast du noch nicht ausgerollt, mach stattdessen in der Anleitung weiter, aus der du gekommen bist.

7. Ausrollen

Nur wenn plan in Schritt 6 Änderungen gezeigt hat, in jedem betroffenen Ordner:

Bash
cue cmd apply

Erwartet: dieselbe Ausgabe wie bei plan, dann Continue? (yes/no); tippe yes.

8. Pushen

Bash
git push

Erwartet: am Ende eine Zeile main -> main. Wird der Push wieder abgelehnt, hat in der Zwischenzeit noch jemand gepusht; fang wieder mit Schritt 1 an.

Prüfen

  • git status zeigt Your branch is up to date with 'origin/main'. und nothing to commit, working tree clean.
  • git log --oneline -5 zeigt die Commits der anderen und darüber deine eigenen, ohne Merge-Commit.
  • cue cmd plan zeigt in jedem Ordner aus Schritt 5 „No changes“.

Wenn etwas schiefgeht