-
1. Kom igång
- 1.1 Om versionshantering
- 1.2 En kort historik om Git
- 1.3 Vad är Git?
- 1.4 Kommandoraden
- 1.5 Installera Git
- 1.6 Första gången med Git
- 1.7 Få hjälp
- 1.8 Sammanfattning
-
2. Grunderna i Git
- 2.1 Att få tag i ett Git‑kodförråd
- 2.2 Spara ändringar i kodförrådet
- 2.3 Visa incheckningshistoriken
- 2.4 Ångra saker
- 2.5 Arbeta med fjärrkodförråd
- 2.6 Att tagga
- 2.7 Git-alias
- 2.8 Sammanfattning
-
3. Git-grenar
- 3.1 Grenar i korthet
- 3.2 Grundläggande gren- och sammanfogningsarbete
- 3.3 Grenhantering
- 3.4 Arbetsflöden med grenar
- 3.5 Fjärrgrenar
- 3.6 Ombasering
- 3.7 Sammanfattning
-
4. Git på servern
- 4.1 Protokollen
- 4.2 Konfigurera Git på en server
- 4.3 Generera din publika SSH-nyckel
- 4.4 Konfigurera servern
- 4.5 Git-demon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Tredjepartsalternativ
- 4.10 Sammanfattning
-
5. Distribuerat Git
-
6. GitHub
-
7. Git-verktyg
- 7.1 Revisionsurval
- 7.2 Interaktiv köläggning
- 7.3 Lägga undan och städa
- 7.4 Signera ditt arbete
- 7.5 Sökning
- 7.6 Skriva om historik
- 7.7 Nollställning förklarad
- 7.8 Avancerad sammanslagning
- 7.9 Rerere
- 7.10 Felsöka med Git
- 7.11 Undermoduler
- 7.12 Bunta
- 7.13 Ersätt
- 7.14 Lagring av inloggningsuppgifter
- 7.15 Sammanfattning
-
8. Anpassa Git
- 8.1 Git‑konfiguration
- 8.2 Git‑attribut
- 8.3 Git‑krokar
- 8.4 Ett exempel på Git‑upprätthållen policy
- 8.5 Sammanfattning
-
9. Git och andra system
- 9.1 Git som klient
- 9.2 Migrera till Git
- 9.3 Sammanfattning
-
10. Git bakom kulisserna
- 10.1 Lågnivådel och användardel
- 10.2 Git-objekt
- 10.3 Git-referenser
- 10.4 Packfiler
- 10.5 Refspecen
- 10.6 Överföringsprotokoll
- 10.7 Underhåll och dataåterställning
- 10.8 Miljövariabler
- 10.9 Sammanfattning
-
A1. Bilaga A: Git i andra miljöer
- A1.1 Grafiska gränssnitt
- A1.2 Git i Visual Studio
- A1.3 Git i Visual Studio Code
- A1.4 Git i IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git i Sublime Text
- A1.6 Git i Bash
- A1.7 Git i Zsh
- A1.8 Git i PowerShell
- A1.9 Sammanfattning
-
A2. Bilaga B: Bädda in Git i dina applikationer
- A2.1 Git på kommandoraden
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Bilaga C: Git-kommandon
- A3.1 Uppstart och konfiguration
- A3.2 Skaffa och skapa projekt
- A3.3 Grundläggande ögonblicksbilder
- A3.4 Grening och sammanslagning
- A3.5 Dela och uppdatera projekt
- A3.6 Inspektion och jämförelse
- A3.7 Felsökning
- A3.8 Patchning
- A3.9 E‑post
- A3.10 Externa system
- A3.11 Administration
- A3.12 Lågnivåkommandon
7.7 Git-verktyg - Nollställning förklarad
Nollställning förklarad
Innan vi går vidare till mer specialiserade verktyg, låt oss prata om Gits kommandon reset och checkout.
Dessa kommandon är två av de mest förvirrande delarna av Git när du stöter på dem första gången.
De gör så många saker att det verkar hopplöst att verkligen förstå dem och använda dem korrekt.
För detta rekommenderar vi en enkel metafor.
De tre träden
Ett enklare sätt att tänka på reset och checkout är genom att se Git som en innehållsförvaltare av tre olika träd.
Med "träd" menar vi egentligen "samling av filer", inte specifikt datastrukturen.
Det finns några fall där indexet inte riktigt beter sig som ett träd, men för våra syften är det enklast att tänka så här just nu.
Git som system hanterar och manipulerar tre träd i sin normala drift:
| Träd | Roll |
|---|---|
HEAD |
Senaste incheckningsögonblicksbild, nästa förälder |
Index |
Föreslagen nästa incheckningsögonblicksbild |
Arbetskatalog |
Sandlåda |
HEAD
HEAD är pekaren till den aktuella grenreferensen, som i sin tur pekar på den senaste incheckningen som gjorts på den grenen. Det betyder att HEAD blir förälder till nästa incheckning som skapas. Det är i allmänhet enklast att se HEAD som ögonblicksbilden av din senaste incheckning på den grenen.
Faktum är att det är ganska lätt att se hur den ögonblicksbilden ser ut. Här är ett exempel på hur du hämtar den faktiska kataloglistan och SHA‑1-kontrollsummorna för varje fil i HEAD-ögonblicksbilden:
$ git cat-file -p HEAD
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
author Scott Chacon 1301511835 -0700
committer Scott Chacon 1301511835 -0700
initial commit
$ git ls-tree -r HEAD
100644 blob a906cb2a4a904a152... README
100644 blob 8f94139338f9404f2... Rakefile
040000 tree 99f1a6d12cb4b6f19... lib
Kommandona cat-file och ls-tree i Git är lågnivåkommandon som används för lågnivåuppgifter och inte riktigt till vardags, men de hjälper oss att se vad som händer här.
Index
Indexet är din föreslagna nästa incheckning.
Vi har också kallat det här begreppet Gits "köyta" eftersom det är vad Git tittar på när du kör git commit.
Git fyller indexet med en lista över allt filinnehåll som senast lades ut till din arbetskatalog och hur de såg ut när de ursprungligen lades ut.
Sedan ersätter du några av dessa filer med nya versioner av dem, och git commit omvandlar det till trädet för en ny incheckning.
$ git ls-files -s
100644 a906cb2a4a904a152e80877d4088654daad0c859 0 README
100644 8f94139338f9404f26296befa88755fc2598c289 0 Rakefile
100644 47c6340d6459e05787f644c2447d2595f5d3a54b 0 lib/simplegit.rb
Återigen använder vi här git ls-files, som mer är ett bakom kulisserna-kommando som visar hur indexet ser ut just nu.
Indexet är tekniskt sett inte en trädstruktur – det är faktiskt implementerat som ett platt manifest – men för våra syften räcker det gott.
Arbetskatalogen
Till sist har du din arbetskatalog (som ofta också kallas "arbetsträd").
De andra två träden lagrar sitt innehåll på ett effektivt men opraktiskt sätt, inne i katalogen .git.
Arbetskatalogen packar upp dem till faktiska filer, vilket gör det mycket enklare för dig att redigera dem.
Tänk på arbetskatalogen som en sandlåda, där du kan prova ändringar innan du checkar in dem i din köyta (index) och sedan i historiken.
$ tree
.
├── README
├── Rakefile
└── lib
└── simplegit.rb
1 directory, 3 files
Arbetsflödet
Gits typiska arbetsflöde är att spela in ögonblicksbilder av ditt projekt i successivt bättre tillstånd, genom att manipulera dessa tre träd.
Låt oss visualisera processen: säg att du går in i en ny katalog med en enda fil.
Vi kallar detta filens v1, och vi markerar den i blått.
Nu kör vi git init, vilket skapar ett Git‑kodförråd med en HEAD-referens som pekar på den ännu ofödda master-grenen.
I det här läget har bara arbetskatalogträdet något innehåll.
Nu vill vi checka in den här filen, så vi använder git add för att ta innehållet i arbetskatalogen och kopiera det till indexet.
git add
Sedan kör vi git commit, som tar innehållet i indexet och sparar det som en permanent ögonblicksbild, skapar ett incheckningsobjekt som pekar på den ögonblicksbilden och uppdaterar master till att peka på den incheckningen.
git commit
Om vi kör git status ser vi inga ändringar, eftersom alla tre träd är desamma.
Nu vill vi göra en ändring i den filen och checka in den. Vi går igenom samma process; först ändrar vi filen i vår arbetskatalog. Vi kallar detta filens v2, och markerar den i rött.
Om vi kör git status just nu ser vi filen i rött som "Ändringar som inte är köade för incheckning", eftersom den posten skiljer sig mellan indexet och arbetskatalogen.
Nästa steg är att köa den med git add.
Vid det här laget, om vi kör git status, ser vi filen i grönt under "Ändringar att checka in" eftersom indexet och HEAD skiljer sig — det vill säga, vår föreslagna nästa incheckning skiljer sig nu från vår senaste incheckning.
Till sist kör vi git commit för att färdigställa incheckningen.
git commit med ändrad filNu kommer git status inte att ge någon utdata, eftersom alla tre träd är identiska igen.
Att byta grenar eller klona går igenom en liknande process. När du växlar till en gren ändrar det HEAD till att peka på den nya grenreferensen, fyller ditt index med ögonblicksbilden av den incheckningen och kopierar sedan innehållet i indexet till din arbetskatalog.
Nollställning-kommandots roll
Kommandot reset blir mer begripligt när man ser det i detta sammanhang.
För att exemplen ska bli tydligare, låt oss säga att vi har ändrat file.txt igen och checkat in den en tredje gång.
Så nu ser historiken ut så här:
Låt oss nu gå igenom exakt vad reset gör när du anropar det.
Det manipulerar direkt dessa tre träd på ett enkelt och förutsägbart sätt.
Det gör upp till tre grundläggande operationer.
Steg 1: flytta HEAD
Det första reset gör är att flytta vad HEAD pekar på.
Det är inte samma sak som att ändra HEAD självt (vilket är vad checkout gör); reset flyttar grenen som HEAD pekar på.
Det innebär att om HEAD är inställd på grenen master (det vill säga att du för närvarande står på master), kommer git reset 9e5e6a4 att börja med att göra att master pekar på 9e5e6a4.
Oavsett vilken variant av reset med en incheckning du anropar är detta det första den alltid försöker göra.
Med reset --soft stannar den helt enkelt där.
Ta nu en sekund och titta på diagrammet och inse vad som hände: den ångrade i praktiken det senaste git commit-kommandot.
När du kör git commit skapar Git en ny incheckning och flyttar grenen som HEAD pekar på till den.
När du gör reset tillbaka till HEAD~ (föräldern till HEAD) flyttar du grenen tillbaka till där den var, utan att ändra indexet eller arbetskatalogen.
Du kan nu uppdatera indexet och köra git commit igen för att åstadkomma det som git commit --amend skulle ha gjort (se Ändra den senaste incheckningen).
Steg 2: uppdatera indexet (--mixed)
Observera att om du kör git status nu ser du i grönt skillnaden mellan indexet och vad den nya HEAD är.
Nästa sak reset gör är att uppdatera indexet med innehållet i den ögonblicksbild som HEAD nu pekar på.
Om du anger --mixed-flaggan stannar reset vid denna punkt.
Det är också standard, så om du inte anger någon flagga alls (i det här fallet bara git reset HEAD~) stannar kommandot här.
Ta nu en sekund till att titta på diagrammet och inse vad som hände: det ångrade fortfarande din senaste incheckning, men gjorde också allting okölagt.
Du rullade tillbaka till tiden innan du körde dina git add- och git commit-kommandon.
Steg 3: uppdatera arbetskatalogen (--hard)
Den tredje saken reset gör är att få arbetskatalogen att se ut som indexet.
Om du använder --hard fortsätter den till detta steg.
Så låt oss tänka igenom vad som hände.
Du ångrade din senaste incheckning, git add och git commit-kommandona, och allt arbete du gjorde i din arbetskatalog.
Det är viktigt att notera att flaggan (--hard) är det enda sättet att göra kommandot reset farligt, och ett av de få tillfällen där Git faktiskt kan förstöra data.
Alla andra anrop av reset kan relativt enkelt ångras, men flaggan --hard kan det inte, eftersom den med tvång skriver över filer i arbetskatalogen.
I det här fallet har vi fortfarande v3-versionen av vår fil i en incheckning i vår Git‑databas, och vi skulle kunna få tillbaka den genom att titta i vår reflog, men om vi inte hade checkat in den skulle Git ändå ha skrivit över filen och den hade varit omöjlig att återställa.
Sammanfattning
Kommandot reset skriver över dessa tre träd i en specifik ordning och stannar där du säger till:
-
Flytta grenen som HEAD pekar på (stanna här om
--soft). -
Få indexet att se ut som HEAD (stanna här om inte
--hard). -
Få arbetskatalogen att se ut som indexet.
Nollställning med en sökväg
Det täcker beteendet hos reset i sin grundform, men du kan också ange en sökväg som kommandot ska påverka.
Om du anger en sökväg kommer reset att hoppa över steg 1 och begränsa resten av sina åtgärder till en specifik fil eller uppsättning filer.
Det är faktiskt ganska rimligt — HEAD är bara en pekare, och du kan inte peka på en del av en incheckning och en annan del av en annan.
Men indexet och arbetskatalogen kan uppdateras delvis, så reset fortsätter med steg 2 och 3.
Så, anta att vi kör git reset file.txt.
Den formen (eftersom du inte angav ett inchecknings-SHA‑1 eller en gren, och du inte angav --soft eller --hard) är en förkortning för git reset --mixed HEAD file.txt, vilket:
-
Flytta grenen som HEAD pekar på (hoppa över) .
-
Få indexet att se ut som HEAD (stanna här) .
Så den kopierar i praktiken bara file.txt från HEAD till indexet.
Detta har den praktiska effekten att filen avköas.
Om vi tittar på diagrammet för det kommandot och tänker på vad git add gör är de exakta motsatser.
Det är anledningen till att utdata från git status föreslår att du kör det här för att avköa en fil (se Ta bort en köad fil för mer om detta).
Vi skulle lika gärna kunna hindra Git från att anta att vi menade "dra data från HEAD" genom att ange en specifik incheckning att hämta den filversionen från.
Vi skulle då köra något som git reset eb43bf file.txt.
Det gör i praktiken samma sak som om vi hade återställt innehållet i filen till v1 i arbetskatalogen, kört git add på den, och sedan återställt den tillbaka till v3 igen (utan att faktiskt gå igenom alla de stegen).
Om vi kör git commit nu kommer det att registrera en ändring som återställer filen till v1, även om vi aldrig faktiskt hade den i arbetskatalogen igen.
Det är också intressant att notera att likt git add accepterar kommandot reset flaggan --patch för att avköa innehåll diffstycke för diffstycke.
Så du kan selektivt avköa eller återställa innehåll.
Sammanfoga incheckningar
Låt oss titta på hur du kan göra något intressant med den här nyfunna kraften — sammanfoga incheckningar.
Säg att du har en serie incheckningar med meddelanden som "oops.", "WIP" och "forgot this file".
Du kan använda reset för att snabbt och enkelt sammanfoga dem till en enda incheckning som får dig att se riktigt smart ut.
Sammanfoga incheckningar visar ett annat sätt att göra detta, men i det här exemplet är det enklare att använda reset.
Låt oss säga att du har ett projekt där den första incheckningen har en fil, den andra incheckningen lade till en ny fil och ändrade den första, och den tredje incheckningen ändrade den första filen igen. Den andra incheckningen var ett arbete på gång och du vill sammanfoga den.
Du kan köra git reset --soft HEAD~2 för att flytta HEAD-grenen tillbaka till en äldre incheckning (den senaste incheckningen du vill behålla):
Och sedan helt enkelt köra git commit igen:
Nu kan du se att din nåbara historik, den historik du skulle skicka, nu ser ut som att du hade en incheckning med file-a.txt v1, och sedan en andra som både ändrade file-a.txt till v3 och lade till file-b.txt.
Incheckningen med v2-versionen av filen finns inte längre i historiken.
Växla till det
Till sist kanske du undrar vad skillnaden är mellan checkout och reset.
Likt reset manipulerar checkout de tre träden, och det är lite olika beroende på om du ger kommandot en sökväg eller inte.
Utan sökvägar
Att köra git checkout [gren] är ganska likt att köra git reset --hard [gren] genom att det uppdaterar alla tre träd så att de ser ut som [gren], men det finns två viktiga skillnader.
För det första, till skillnad från reset --hard, är checkout säkert för arbetskatalogen; den kontrollerar att den inte skriver över filer som har ändringar i sig.
Faktum är att den är lite smartare än så — den försöker göra en trivialsammanfogning i arbetskatalogen, så alla filer du inte har ändrat blir uppdaterade.
reset --hard kommer däremot helt enkelt ersätta allt utan att kontrollera.
Den andra viktiga skillnaden är hur checkout uppdaterar HEAD.
Medan reset flyttar grenen som HEAD pekar på, flyttar checkout själva HEAD till att peka på en annan gren.
Till exempel, säg att vi har grenarna master och develop som pekar på olika incheckningar, och att vi står på develop (så HEAD pekar på den).
Om vi kör git reset master kommer develop nu att peka på samma incheckning som master gör.
Om vi i stället kör git checkout master flyttar sig inte develop, det gör däremot HEAD självt.
HEAD kommer nu att peka på master.
Så i båda fallen flyttar vi HEAD till att peka på incheckning A, men hur vi gör det är väldigt olika.
reset flyttar grenen som HEAD pekar på, checkout flyttar själva HEAD.
git checkout och git reset
Med sökvägar
Det andra sättet att köra checkout är med en sökväg, vilket, precis som reset, inte flyttar HEAD.
Det är precis som git reset [gren] fil i att det uppdaterar indexet med den filen från den incheckningen, men det skriver också över filen i arbetskatalogen.
Det skulle vara exakt som git reset --hard [gren] fil (om reset skulle låta dig köra det) — det är inte säkert för arbetskatalogen och flyttar inte HEAD.
Dessutom, precis som git reset och git add, accepterar checkout flaggan --patch för att låta dig selektivt återställa filinnehåll diffstycke för diffstycke.
Sammanfattning
Förhoppningsvis förstår du nu och känner dig mer bekväm med kommandot reset, men du är sannolikt fortfarande lite förvirrad över hur exakt det skiljer sig från checkout och kan knappast minnas alla regler för de olika varianterna.
Här är en fusklapp för vilka kommandon som påverkar vilka träd. Kolumnen "HEAD" visar "REF" om det kommandot flyttar referensen (grenen) som HEAD pekar på, och "HEAD" om det flyttar HEAD självt. Var särskilt uppmärksam på kolumnen "AK säker?" — om den säger NEJ, stanna upp och fundera innan du kör kommandot.
| HEAD | Index | Arbetskatalog | AK säker? | |
|---|---|---|---|---|
Incheckningsnivå |
||||
|
REF |
NEJ |
NEJ |
JA |
|
REF |
JA |
NEJ |
JA |
|
REF |
JA |
JA |
NEJ |
|
HEAD |
JA |
JA |
JA |
Filnivå |
||||
|
NEJ |
JA |
NEJ |
JA |
|
NEJ |
JA |
JA |
NEJ |