-
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.6 Git-verktyg - Skriva om historik
Skriva om historik
Många gånger när du arbetar med Git vill du revidera din lokala incheckningshistorik.
En av de fina sakerna med Git är att du kan ta beslut i sista möjliga stund.
Du kan bestämma vilka filer som ska hamna i vilka incheckningar precis innan du checkar in med hjälp av köytan, du kan bestämma att du egentligen inte skulle arbeta med något ännu med git stash, och du kan skriva om incheckningar som redan hänt så att de ser ut att ha hänt på ett annat sätt.
Det kan handla om att ändra ordningen på incheckningar, ändra meddelanden eller modifiera filer i en incheckning, sammanfoga eller dela upp incheckningar, eller ta bort incheckningar helt och hållet — allt innan du delar ditt arbete med andra.
I det här avsnittet ser du hur du genomför dessa uppgifter så att du kan få din incheckningshistorik att se ut som du vill innan du delar den.
|
Notera
|
Skicka inte upp ditt arbete förrän du är nöjd med det
En av Gits viktigaste regler är att eftersom så mycket arbete är lokalt i din klon har du en stor frihet att skriva om din historik lokalt. När du väl har skickat upp ditt arbete är det en helt annan sak, och du bör se uppskickat arbete som slutgiltigt om du inte har goda skäl att ändra det. Kort sagt bör du undvika att skicka ditt arbete innan du är nöjd med det och redo att dela det med resten av världen. |
Ändra den senaste incheckningen
Att ändra din senaste incheckning är troligen den vanligaste omskrivningen av historik du gör. Ofta vill du göra två grundläggande saker med din senaste incheckning: helt enkelt ändra incheckningsmeddelandet, eller ändra själva innehållet i incheckningen genom att lägga till, ta bort och ändra filer.
Om du bara vill ändra ditt senaste incheckningsmeddelande är det enkelt:
$ git commit --amend
Kommandot ovan läser in tidigare incheckningsmeddelandet i en redigerarsession, där du kan göra ändringar i meddelandet, spara dem och avsluta. När du sparar och stänger redigeraren skriver den en ny incheckning som innehåller det uppdaterade meddelandet och gör det till din nya senaste incheckning.
Om du i stället vill ändra det faktiska innehållet i din senaste incheckning fungerar processen i princip på samma sätt — gör först de ändringar du tror att du glömde, köa dem, och det efterföljande git commit --amend ersätter den senaste incheckningen med din nya, förbättrade incheckning.
Du måste vara försiktig med den här tekniken eftersom en amend ändrar SHA-1 för incheckningen. Det är som en mycket liten ombasering — ändra inte din senaste incheckning om du redan har skickat upp den.
|
Tips
|
En ändrad incheckning kan (eller kan inte) behöva ett ändrat meddelande
När du ändrar en incheckning har du möjlighet att ändra både incheckningsmeddelandet och innehållet i incheckningen. Om du ändrar innehållet i incheckningen i stor utsträckning bör du nästan säkert uppdatera incheckningsmeddelandet så att det speglar det ändrade innehållet. Å andra sidan, om dina ändringar är tillräckligt triviala (fixa ett fånigt stavfel eller lägga till en fil du glömde att köa) så att det tidigare incheckningsmeddelandet är helt okej, kan du helt enkelt göra ändringarna, köa dem och undvika en onödig redigerarsession genom:
|
Ändra flera incheckningsmeddelanden
För att ändra en incheckning som ligger längre bak i historiken måste du använda mer avancerade verktyg.
Git har inget verktyg som heter "historikändring", men du kan använda ombaseringsverktyget för att ombasera en serie incheckningar på den HEAD som de ursprungligen baserades på i stället för att flytta dem till en annan.
Med interaktiv ombasering kan du sedan stanna efter varje incheckning du vill ändra och ändra meddelandet, lägga till filer eller göra vad du vill.
Du kan köra ombasering interaktivt genom att lägga till flaggan -i till git rebase.
Du måste ange hur långt tillbaka du vill skriva om incheckningar genom att tala om för kommandot vilken incheckning som ska vara bas för ombaseringen.
Till exempel, om du vill ändra de tre senaste incheckningsmeddelandena, eller något av incheckningsmeddelandena i den gruppen, anger du som argument till git rebase -i föräldern till den senaste incheckningen du vill redigera, vilket är HEAD~2^ eller HEAD~3.
Det kan vara lättare att komma ihåg ~3 eftersom du försöker redigera de tre senaste incheckningarna, men tänk på att du faktiskt pekar ut föräldern till den senaste incheckningen du vill redigera, vilket är fyra incheckningar tillbaka:
$ git rebase -i HEAD~3
Kom ihåg att detta är ett ombaseringskommando — varje incheckning i intervallet HEAD~3..HEAD med ett ändrat meddelande och alla dess efterföljare kommer att skrivas om.
Ta inte med någon incheckning som du redan har skickat upp till en central server — det kommer att förvirra andra utvecklare genom att ge en alternativ version av samma ändring.
När du kör kommandot får du en lista över incheckningar i din textredigerare som ser ut ungefär så här:
pick f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Det är viktigt att förstå att dessa incheckningar listas i motsatt ordning än du normalt ser dem med log-kommandot.
Om du kör log ser du något som detta:
$ git log --pretty=format:"%h %s" HEAD~3..HEAD
a5f4a0d Add cat-file
310154e Update README formatting and add blame
f7f3f6d Change my name a bit
Lägg märke till den omvända ordningen.
Den interaktiva ombaseringen ger dig ett skript som den kommer att köra.
Den börjar vid incheckningen du anger på kommandoraden (HEAD~3) och spelar upp ändringarna som introducerades i varje av dessa incheckningar från topp till botten.
Den listar den äldsta högst upp i stället för den nyaste, eftersom det är den första den spelar upp.
Du måste redigera skriptet så att det stannar vid incheckningen du vill ändra. För att göra det ändrar du ordet "pick" till ordet "edit" för varje incheckning du vill att skriptet ska stanna efter. Till exempel, för att ändra bara det tredje incheckningsmeddelandet, ändrar du filen så att den ser ut så här:
edit f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
När du sparar och lämnar redigeraren spolar Git tillbaka dig till den senaste incheckningen i listan och lämnar dig på kommandoraden med följande meddelande:
$ git rebase -i HEAD~3
Stopped at f7f3f6d... Change my name a bit
You can amend the commit now, with
git commit --amend
Once you're satisfied with your changes, run
git rebase --continue
Dessa instruktioner talar om exakt vad du ska göra. Skriv:
$ git commit --amend
Ändra incheckningsmeddelandet och lämna redigeraren. Kör sedan:
$ git rebase --continue
Det här kommandot kommer att applicera de andra två incheckningarna automatiskt, och sedan är du klar.
Om du ändrar pick till edit på fler rader kan du upprepa dessa steg för varje incheckning du ändrar till edit.
Varje gång kommer Git att stanna, låta dig ändra incheckningen och fortsätta när du är klar.
Ändra ordningen på incheckningar
Du kan också använda interaktiva ombaseringar för att ändra ordningen eller ta bort incheckningar helt. Om du vill ta bort incheckningen "Lägg till cat-file" och ändra ordningen som de andra två incheckningarna introduceras i, kan du ändra ombaseringsskriptet från detta:
pick f7f3f6d Change my name a bit
pick 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
till detta:
pick 310154e Update README formatting and add blame
pick f7f3f6d Change my name a bit
När du sparar och lämnar redigeraren spolar Git tillbaka din gren till föräldern till dessa incheckningar, applicerar 310154e och sedan f7f3f6d, och stannar sedan.
Du ändrar effektivt ordningen på dessa incheckningar och tar bort incheckningen "Lägg till cat-file" helt.
Sammanfoga incheckningar
Det går också att ta en serie incheckningar och sammanfoga dem till en enda incheckning med interaktiv ombasering. Skriptet lägger in hjälpsamma instruktioner i ombaseringsmeddelandet:
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Om du i stället för "pick" eller "edit" anger "pressa" applicerar Git både den ändringen och ändringen direkt före den och låter dig sammanfoga incheckningsmeddelandena. Om du alltså vill skapa en enda incheckning från dessa tre incheckningar gör du så att skriptet ser ut så här:
pick f7f3f6d Change my name a bit
squash 310154e Update README formatting and add blame
squash a5f4a0d Add cat-file
När du sparar och lämnar redigeraren applicerar Git alla tre ändringar och återför dig sedan till redigeraren för att sammanfoga de tre incheckningsmeddelandena:
# This is a combination of 3 commits.
# The first commit's message is:
Change my name a bit
# This is the 2nd commit message:
Update README formatting and add blame
# This is the 3rd commit message:
Add cat-file
När du sparar det får du en enda incheckning som introducerar ändringarna från alla tre tidigare incheckningar.
Dela upp en incheckning
Att dela upp en incheckning innebär att du ångrar en incheckning och sedan köar och checkar in delarna så många gånger som behövs för att få det antal incheckningar du vill ha.
Till exempel, anta att du vill dela upp den mittersta incheckningen av dina tre incheckningar.
I stället för "Update README formatting and add blame" vill du dela upp den i två incheckningar: "Update README formatting" för den första och "Add blame" för den andra.
Du kan göra det i rebase -i-skriptet genom att ändra instruktionen för incheckningen du vill dela upp till "edit":
pick f7f3f6d Change my name a bit
edit 310154e Update README formatting and add blame
pick a5f4a0d Add cat-file
Sedan, när skriptet lämnar dig i kommandoraden, återställer du den incheckningen, tar de ändringar som återställts och skapar flera incheckningar av dem.
När du sparar och lämnar redigeraren spolar Git tillbaka till föräldern till den första incheckningen i din lista, applicerar den första incheckningen (f7f3f6d), applicerar den andra (310154e) och släpper dig till terminalen.
Där kan du göra en blandad återställning av den incheckningen med git reset HEAD^, vilket i praktiken ångrar den incheckningen och lämnar de ändrade filerna ej köade.
Nu kan du köa och checka in filer tills du har flera incheckningar och köra git rebase --continue när du är klar:
$ git reset HEAD^
$ git add README
$ git commit -m 'Update README formatting'
$ git add lib/simplegit.rb
$ git commit -m 'Add blame'
$ git rebase --continue
Git applicerar den sista incheckningen (a5f4a0d) i skriptet, och din historik ser ut så här:
$ git log -4 --pretty=format:"%h %s"
1c002dd Add cat-file
9b29157 Add blame
35cfb2b Update README formatting
f7f3f6d Change my name a bit
Detta ändrar SHA-1 för de tre senaste incheckningarna i listan, så se till att ingen incheckning du redan har skickat upp finns med där.
Lägg märke till att den sista incheckningen (f7f3f6d) i listan är oförändrad.
Trots att den här incheckningen visas i skriptet, eftersom den var markerad som "pick" och applicerades före ombaseringen, lämnar Git incheckningen oförändrad.
Ta bort en incheckning
Om du vill bli av med en incheckning kan du ta bort den med rebase -i-skriptet.
I listan över incheckningar skriver du ordet "drop" före incheckningen du vill ta bort (eller raderar helt enkelt den raden från ombaseringsskriptet):
pick 461cb2a This commit is OK
drop 5aecc10 This commit is broken
På grund av hur Git bygger incheckningsobjekt kommer borttagning eller ändring av en incheckning att skriva om alla incheckningar som följer efter den. Ju längre bak i historiken du går, desto fler incheckningar måste skapas om. Det här kan ge många sammanslagningskonflikter om senare incheckningar i sekvensen bygger på den du just tog bort.
Om du kommer halvvägs in i en ombasering som detta och bestämmer dig för att det inte var en bra idé kan du alltid avbryta.
Skriv git rebase --abort, så återställs ditt kodförråd till det läge det var i innan du började ombasera.
Om du avslutar en ombasering och bestämmer dig för att det inte var vad du ville kan du använda git reflog för att återställa en tidigare version av din gren.
Se Dataåterställning för mer information om reflog-kommandot.
|
Notera
|
Drew DeVault har skrivit en praktisk handbok med övningar för att lära sig |
Kärnvapenalternativet: filtrera-gren
Det finns ett annat alternativ för historikomskrivning som du kan använda om du behöver skriva om ett större antal incheckningar på ett skriptbart sätt – till exempel att ändra din e-postadress globalt eller ta bort en fil från varje incheckning.
Kommandot är filter-branch, och det kan skriva om enorma delar av din historik, så du bör förmodligen inte använda det om ditt projekt ännu inte är publikt och andra människor inte har baserat arbete på de incheckningar du är på väg att skriva om.
Det kan dock vara mycket användbart.
Du får se några vanliga användningar så att du kan få en uppfattning om vad det klarar.
|
Var uppmärksam
|
|
Ta bort en fil från varje incheckning
Det här är ganska vanligt.
Någon råkar checka in en stor binärfil med ett tanklöst git add ., och du vill ta bort den överallt.
Kanske råkade du checka in en fil som innehöll ett lösenord och du vill göra projektet till öppen källkod.
filter-branch är verktyget du sannolikt vill använda för att skrubba hela din historik.
För att ta bort en fil med namnet passwords.txt från hela din historik kan du använda flaggan --tree-filter till filter-branch:
$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' was rewritten
Flaggan --tree-filter kör det angivna kommandot efter varje utläggning av projektet och checkar in sedan resultaten.
I det här fallet tar du bort en fil som heter passwords.txt från varje ögonblicksbild, oavsett om den finns eller inte.
Om du vill ta bort alla av misstag checkade in säkerhetskopior från redigerare kan du köra något som git filter-branch --tree-filter 'rm -f *~' HEAD.
Du kan se Git skriva om träd och incheckningar och sedan flytta grenpekaren i slutet.
Det är i allmänhet en bra idé att göra detta i en testgren och sedan göra en hård återställning av master när du har fastställt att resultatet är det du vill.
För att köra filter-branch på alla dina grenar kan du skicka --all till kommandot.
Gör en underkatalog till ny rot
Anta att du har gjort en import från ett annat versionshanteringssystem och har underkataloger som inte har någon mening (trunk, tags och så vidare).
Om du vill att underkatalogen trunk ska vara den nya projektnoden i varje incheckning kan filter-branch hjälpa dig med det också:
$ git filter-branch --subdirectory-filter trunk HEAD
Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12)
Ref 'refs/heads/master' was rewritten
Nu är din nya projektnod det som fanns i underkatalogen trunk varje gång.
Git tar också automatiskt bort incheckningar som inte påverkade underkatalogen.
Ändra e-postadresser globalt
Ett annat vanligt fall är att du glömde köra git config för att ställa in ditt namn och din e-postadress innan du började arbeta, eller kanske vill du göra ett öppet källkodsprojekt på jobbet och byta alla arbetsadresser till din privata adress.
I vilket fall som helst kan du ändra e-postadresser i flera incheckningar i en batch med filter-branch också.
Du måste vara försiktig så att du bara ändrar de e-postadresser som är dina, så du använder --commit-filter:
$ git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
then
GIT_AUTHOR_NAME="Scott Chacon";
GIT_AUTHOR_EMAIL="schacon@example.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD
Detta går igenom och skriver om varje incheckning så att den får din nya adress. Eftersom incheckningar innehåller SHA-1-värdena för sina föräldrar ändrar det här kommandot varje SHA-1 i din historik, inte bara de som matchar e-postadressen.