Konflikter uten panikk
Den interaktive leksjonen trenger JavaScript. Teksten under er hele leksjonen.
Etter leksjonen kan du
- forklare hvorfor Git stopper med en konflikt når samme linjer er endret ulikt på begge sider forstå
- lese
git statusog konfliktmarkørene under en pågående merge forstå - løse en konflikt ved å bestemme ønsket innhold, markere den med
git addog fullføre medgit commitanvende - avbryte en pågående merge trygt med
git merge --abortanvende
Forstå: Et spørsmål bare du kan svare på
Begge brancher har endret den samme linjen i README.md. På main står det Welcome, new students. På feature står det Welcome to the Git course. Basen hadde bare Welcome.
Git kan ikke vite hvilken tekst dere vil ha. Derfor stopper merge midt i, og filen blir merket som en konflikt. Staging-området holder nå tre versjoner av filen: basen, vår og deres. Ingen merge-commit er laget ennå.
I filen har Git skrevet konfliktmarkører. Fra <<<<<<< HEAD til ======= står din versjon, fra branchen du står på. Fra ======= til >>>>>>> feature står versjonen du merger inn.
Du løser konflikten ved å bestemme hva filen faktisk skal si. Ofte er svaret en blanding av begge. Skriv den ferdige teksten, og fjern alle markørene.
git add forteller Git at konflikten er løst. Den nye teksten erstatter de tre versjonene i staging-området. Git sjekker ikke om løsningen er god, så les filen før du stager den.
git commit fullfører merge-commiten med to foreldre.
Se demo: Stopp, les, bestem
Vi står på main, og begge brancher har endret den første linjen i README.md. Vi merger. Terminalen sier CONFLICT, og kommandoen avslutter med en feilkode.
$ git merge feature
Når noe uventet skjer, les git status først. Den sier at vi er midt i en merge, at README.md er endret på begge sider, og hvordan vi kan fullføre eller avbryte.
$ git status
Her er markørene. Øverst står vår versjon fra HEAD. Under streken står versjonen fra feature.
$ cat README.md
Først øver vi på nødutgangen. Vi startet rent, så git merge --abort tar oss tilbake til nøyaktig der vi var før mergen. Se filområdene: README.md er tilbake til versjonen fra main.
$ git merge --abort
Nå starter vi mergen på nytt. Denne gangen løser vi konflikten.
$ git merge feature
Vi bestemmer at velkomsten skal ha med begge deler. Vi skriver den ferdige teksten, uten markører. Se arbeidsmappen: filen har nå én linje.
$ echo "Welcome to the Git course, new students" > README.md
Nå sier vi fra at konflikten er løst. Se staging-området: konfliktmerket forsvinner, og den nye teksten ligger klar til commit.
$ git add README.md
Til slutt fullfører vi. Git har allerede foreslått meldingen Merge branch feature, så vi lagrer den. Se grafen: merge-commiten har to foreldre.
$ git commit
Oppgaver
- Start en merge av
featureinn imain. - Spør Git hva tilstanden er nå.
- Avbryt mergen, og kom tilbake til der du var før.
- Skriv den ferdige teksten i README.md: begge velkomstene i én linje, uten markører.
- Marker konflikten i README.md som løst.
- Fullfør mergen.
Øv: Overfør: kantinas meny
Du står på main i kantinas repository. Branchene lunch og discount har endret filer som main også har endret. Ingen demo nå — bruk det du har sett.
Oppgaver
- Merge
lunchinn imain. Menyen skal ha med både salat og suppe. Fullfør mergen. - Merge inn
discount. Ledelsen har bestemt at studentprisen 79 skal gjelde. Løs konflikten i tråd med det, og fullfør mergen.
Sjekk deg selv: Hent det fram igjen
Du har redigert konfliktfilen og kjørt git add. Hva har Git kontrollert?
- At løsningen er riktig og programmet virker
- Ingenting. Git har bare registrert innholdet du valgte som løsning.
- At begge versjonene er med i filen
Du står på main og merger inn feature. Hva står mellom <<<<<<< HEAD og =======?
- Versjonen fra
feature - Versjonen fra
main, der du står - Versjonen fra merge-basen
Du er midt i en konflikt og vil heller starte på nytt. Hva er tryggest?
- Slette mappen og klone på nytt
git merge --abortgit add .oggit commit, så konflikten forsvinner
Hva betyr en merge-konflikt?
- At noen har gjort noe feil
- At Git ikke kan kombinere endringene fra begge sider alene, og trenger din avgjørelse
- At begge branchene har endret den samme filen
Repetisjon: Midt i konflikten oppdager du en skrivefeil i en annen fil, som ikke har noe med merge å gjøre. Hva er best?
- Rett den nå, så den blir med i merge-commiten
- Fullfør mergen med bare konfliktløsningen, og rett skrivefeilen i en egen commit etterpå
- Vent til du har samlet flere småting, og commit alt sammen senere
Vanlige misforståelser
- «En konflikt betyr at noe er ødelagt.» Den er et spørsmål Git ikke kan svare på alene. Historikken er urørt.
- «
git addsjekker at konflikten er riktig løst.» Den registrerer bare innholdet du valgte, også om markørene fortsatt står der. - «Jeg må velge enten min eller din versjon.» Løsningen er det dere faktisk vil ha. Ofte er det en blanding.
Les mer
Hva Git stoppet på
En konflikt oppstår når begge sider har endret samme område av en fil ulikt siden merge-basen. Det finnes også andre typer, og git status --short merker hver av dem:
| Kode | Betydning |
|---|---|
UU |
begge har endret de samme linjene (innholdskonflikt) |
AA |
begge har lagt til en fil med samme navn, men ulikt innhold |
DU / UD |
den ene siden slettet en fil som den andre endret |
Merge avslutter med status 1 og forteller hvilke filer som trenger deg:
$ git merge feature
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
Alle filer uten konflikt er allerede merget og staget. Bare filene i listen venter på deg.
Les tilstanden før du rører noe
$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: README.md
git status sier alltid hvilken operasjon som pågår, og hvilke kommandoer som fullfører eller avbryter den. Det betyr noe, for konflikter i rebase og cherry-pick ser like ut, men fullføres på en annen måte.
Tre versjoner i staging-området
Under en konflikt holder staging-området (index) opptil tre versjoner av filen, kalt stages:
$ git ls-files -u
100644 ba4c5142cc… 1 README.md
100644 b489365621… 2 README.md
100644 4b854ebe86… 3 README.md
Stage 1 er basen, 2 er vår side (HEAD) og 3 er deres side (branchen som merges inn). Du kan skrive ut hver av dem, for eksempel git show :1:README.md for basen. git add README.md erstatter de tre versjonene med én vanlig oppføring. Det er alt «markere som løst» betyr: Git ser ikke på hva du skrev.
Se basen i markørene
Som standard viser markørene bare vår og deres side. Slå på basen også:
git config --global merge.conflictStyle diff3
Neste konflikt viser hvordan linjen så ut før noen av sidene endret den:
<<<<<<< HEAD
Welcome, new students
||||||| 0483acd
Welcome
=======
Welcome to the Git course
>>>>>>> feature
Nå er hensikten tydelig: begge sider utvidet «Welcome», så svaret er trolig å beholde begge tilleggene. zdiff3 er en mer kompakt variant av samme stil.
Ta én hel side
Når du virkelig vil ha én versjon av en hel fil, trenger du ikke redigere for hånd:
git checkout --ours README.mdellergit checkout --theirs README.mdskriver den siden til arbeidsmappen.git restore --oursoggit restore --theirsgjør det samme.git checkout -m README.mdhenter tilbake konfliktmarkørene hvis du har rotet det til og vil starte filen på nytt.
Stage deretter filen som vanlig. For -X ours og -X theirs, som avgjør alle konflikter på forhånd, se leksjonen om treveis merge.
Sjekk før du committer
git add stager gjerne en fil som fortsatt har markører. To sikkerhetsnett:
$ git diff --cached --check
README.md:1: leftover conflict marker
README.md:3: leftover conflict marker
README.md:5: leftover conflict marker
Og git commit nekter så lenge en fil fortsatt ikke er merget:
error: Committing is not possible because you have unmerged files.
Ingen av dem sjekker at resultatet er riktig. Les filen, bygg og kjør testene.
Fullfør eller gå tilbake
git commitellergit merge --continuefullfører merge-commiten med to foreldre. De gjør det samme.git merge --abortgår tilbake til tilstanden før mergen. Det fungerer pålitelig bare hvis du startet rent. Hadde du endringer som ikke var committet, er det ikke sikkert Git klarer å gjenopprette dem. Derfor nekter Git å starte en merge som ville overskrevet lokale endringer: «Please commit your changes or stash them before you merge.»
Merge-verktøy
git mergetool åpner et treveis merge-verktøy, grafisk eller i terminalen, for eksempel Meld, KDiff3, vimdiff eller VS Code, for hver fil i konflikt, med base, vår og deres side ved siden av hverandre. git mergetool --tool-help lister verktøyene Git finner på maskinen din. Noen verktøy legger igjen *.orig-sikkerhetskopier; git config --global mergetool.keepBackup false slår det av. Nettleser-sandkassen har ingen merge-verktøy, men på din egen maskin hjelper de ved store konflikter.
Konflikter i rebase og cherry-pick
Markørene og stagene er de samme, men du fullfører med kommandoen som startet operasjonen:
$ git rebase main
CONFLICT (content): Merge conflict in README.md
error: could not apply 01d2650... Name the course in the greeting
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
Bruk git rebase --continue, git rebase --skip eller git rebase --abort, og git cherry-pick --continue eller git cherry-pick --abort. Vær obs på at sidene bytter plass under en rebase: «ours» er branchen du rebaser over på, og «theirs» er din egen commit som spilles av på nytt.
Løse samme konflikt igjen: rerere
Møter du den samme konflikten gang på gang, for eksempel når du rebaser en langlivet branch mange ganger, slå på «reuse recorded resolution»:
git config --global rerere.enabled true
Git husker hvordan du løste hver konflikt, og bruker samme løsning neste gang:
Recorded resolution for 'README.md'.
...
Resolved 'README.md' using previous resolution.
Filen oppdateres, men stages ikke. Sjekk den, og kjør git add som vanlig.
Færre og mindre konflikter
- Merge ofte. Én dag med avstand gir små konflikter; en måned gir store.
- Hold commits fokuserte. Ikke bland omformatering av en hel fil med ekte endringer.
- Bli enige om en formaterer, så mellomrom og stil ikke kolliderer.
- Snakk med personen hvis endring du kombinerer med. Riktig løsning er et spørsmål om hensikt, ikke om tekst.
Per-Arne's Git Good!