-
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.13 Git-verktyg - Ersätt
Ersätt
Som vi har betonat tidigare är objekten i Gits objektdatabas oföränderliga, men Git erbjuder ett intressant sätt att låtsas ersätta objekt i databasen med andra objekt.
Kommandot replace låter dig ange ett objekt i Git och säga "varje gång du refererar till detta objekt, låtsas att det är ett annat objekt".
Det här är oftast användbart för att ersätta en incheckning i din historik med en annan utan att behöva bygga om hela historiken med till exempel git filter-branch.
Till exempel, säg att du har en enorm kodhistorik och vill dela upp ditt kodförråd i en kort historik för nya utvecklare och en mycket längre och större historik för personer som är intresserade av datautvinning. Du kan ympa den ena historiken på den andra genom att "ersätta" den tidigaste incheckningen i den nya linjen med den senaste incheckningen i den äldre. Det är trevligt eftersom det betyder att du faktiskt inte behöver skriva om varje incheckning i den nya historiken, som du annars normalt måste göra för att sammanfoga dem (eftersom föräldraskapet påverkar SHA‑1‑värdena).
Låt oss prova.
Vi tar ett befintligt kodförråd, delar upp det i två kodförråd, ett nyligt och ett historiskt, och ser sedan hur vi kan kombinera dem igen utan att ändra SHA‑1‑värdena i det nyare kodförrådet med hjälp av replace.
Vi använder ett enkelt kodförråd med fem enkla incheckningar:
$ git log --oneline
ef989d8 Fifth commit
c6e1e95 Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
Vi vill dela upp det här i två historiklinjer. Den ena linjen går från första incheckningen till den fjärde — det blir den historiska. Den andra linjen är bara incheckning fyra och fem — det blir den nyare historiken.
Att skapa den historiska historiken är enkelt; vi kan bara lägga en gren i historiken och sedan skicka den grenen till master i ett nytt fjärrkodförråd.
$ git branch history c6e1e95
$ git log --oneline --decorate
ef989d8 (HEAD, master) Fifth commit
c6e1e95 (history) Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
history-grenNu kan vi skicka den nya history-grenen till master i vårt nya kodförråd:
$ git remote add project-history https://github.com/schacon/project-history
$ git push project-history history:master
Counting objects: 12, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (12/12), 907 bytes, done.
Total 12 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
To git@github.com:schacon/project-history.git
* [new branch] history -> master
Okej, så vår historik är publicerad. Nu är den svårare delen att kapa vår nyare historik så att den blir mindre. Vi behöver en överlappning så att vi kan ersätta en incheckning i den ena med en motsvarande incheckning i den andra, så vi kapar till bara incheckning fyra och fem (så att incheckning fyra överlappar).
$ git log --oneline --decorate
ef989d8 (HEAD, master) Fifth commit
c6e1e95 (history) Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
Det är användbart att skapa en basincheckning med instruktioner om hur historiken kan byggas ut, så att andra utvecklare vet vad de ska göra om de stöter på den första incheckningen i den kapade historiken och behöver mer. Så här gör vi: vi skapar ett inledande incheckningsobjekt som baspunkt med instruktioner, och sedan ombaserar vi de återstående incheckningarna (fyra och fem) ovanpå den.
För att göra det måste vi välja en punkt att dela vid, vilket för oss är den tredje incheckningen, 9c68fdc i SHA‑språk.
Så vår basincheckning kommer att baseras på det trädet.
Vi kan skapa vår basincheckning med kommandot commit-tree, som bara tar ett träd och ger oss ett helt nytt, föräldralöst incheckningsobjekt tillbaka.
$ echo 'Get history from blah blah blah' | git commit-tree 9c68fdc^{tree}
622e88e9cbfbacfb75b5279245b9fb38dfea10cf
|
Notera
|
Kommandot |
commit-tree
Okej, nu när vi har en basincheckning kan vi ombasera resten av vår historik ovanpå den med git rebase --onto.
Argumentet --onto kommer att vara SHA‑1:an vi just fick tillbaka från commit-tree och ombaseringspunkten blir den tredje incheckningen (föräldern till den första incheckningen vi vill behålla, 9c68fdc).
$ git rebase --onto 622e88 9c68fdc
First, rewinding head to replay your work on top of it...
Applying: fourth commit
Applying: fifth commit
Okej, så nu har vi skrivit om vår nyare historik ovanpå en kastad basincheckning som nu har instruktioner i sig om hur man återskapar hela historiken om man vill. Vi kan skicka den nya historiken till ett nytt projekt och nu när folk klonar det kodförrådet ser de bara de två senaste incheckningarna och sedan en basincheckning med instruktioner.
Nu byter vi roll till någon som klonar projektet för första gången och vill ha hela historiken. För att få historikdata efter att ha klonat det kapade kodförrådet behöver man lägga till ett andra fjärrkodförråd för det historiska kodförrådet och uppdatera:
$ git clone https://github.com/schacon/project
$ cd project
$ git log --oneline master
e146b5f Fifth commit
81a708d Fourth commit
622e88e Get history from blah blah blah
$ git remote add project-history https://github.com/schacon/project-history
$ git fetch project-history
From https://github.com/schacon/project-history
* [new branch] master -> project-history/master
Nu har medarbetaren sina nyare incheckningar i master‑grenen och de historiska incheckningarna i project-history/master.
$ git log --oneline master
e146b5f Fifth commit
81a708d Fourth commit
622e88e Get history from blah blah blah
$ git log --oneline project-history/master
c6e1e95 Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
För att kombinera dem kan du helt enkelt kalla git replace med incheckningen du vill ersätta och sedan incheckningen du vill ersätta den med.
Vi vill alltså ersätta den "fjärde" incheckningen i master‑grenen med den "fjärde" incheckningen i project-history/master:
$ git replace 81a708d c6e1e95
Om du nu tittar på historiken i master‑grenen ser det ut att vara så här:
$ git log --oneline master
e146b5f Fifth commit
81a708d Fourth commit
9c68fdc Third commit
945704c Second commit
c1822cf First commit
Coolt, eller hur? Utan att behöva ändra alla SHA‑1‑värden uppströms kunde vi ersätta en incheckning i historiken med en helt annan incheckning och alla normala verktyg (bisect, blame, osv.) kommer att fungera som vi förväntar oss.
git replace
Intressant nog visar den fortfarande 81a708d som SHA‑1, även om den i själva verket använder data från c6e1e95‑incheckningen som vi ersatte den med.
Även om du kör ett kommando som cat-file kommer det att visa dig de ersatta uppgifterna:
$ git cat-file -p 81a708d
tree 7bc544cf438903b65ca9104a1e30345eee6c083d
parent 9c68fdceee073230f19ebb8b5e7fc71b479c0252
author Scott Chacon <schacon@gmail.com> 1268712581 -0700
committer Scott Chacon <schacon@gmail.com> 1268712581 -0700
fourth commit
Kom ihåg att den faktiska föräldern till 81a708d var vår platshållarincheckning (622e88e), inte 9c68fdce som det står här.
En annan intressant sak är att denna data sparas i våra referenser:
$ git for-each-ref
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/heads/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/remotes/history/master
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/HEAD
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/replace/81a708dd0e167a3f691541c7a6463343bc457040
Det betyder att det är lätt att dela vår ersättning med andra, eftersom vi kan skicka detta till vår server och andra kan enkelt ladda ner den. Det är inte så hjälpsamt i det historikympningsscenario vi har gått igenom här (eftersom alla ändå skulle uppdatera båda historikerna, så varför separera dem?) men det kan vara användbart i andra sammanhang.