Branches vermenigvuldigen de basisfunctionaliteit die wordt aangeboden door commits door gebruikers in staat te stellen hun geschiedenis te vervuilen. Het creëren van een nieuwe vestiging is vergelijkbaar met het aanvragen van een nieuwe ontwikkelomgeving, compleet met een geïsoleerde werkdirectory, een staging area en projectgeschiedenis.
Dit geeft u dezelfde gemoedsrust als het uitvoeren van een "veilige" kopie van uw project, maar u hebt nu de extra capaciteit om tegelijkertijd aan meerdere versies te werken. Takken inschakelen a niet-lineaire workflow-het vermogen om niet-gerelateerde functies parallel te ontwikkelen. Zoals we later zullen ontdekken, is een niet-lineaire workflow een belangrijke voorbode van de gedistribueerde aard van het samenwerkingsmodel van Git.
In tegenstelling tot SVN of CVS is de filiaalimplementatie van Git ongelooflijk efficiënt. SVN maakt filialen mogelijk door het hele project naar een nieuwe map te kopiëren, net zoals je zou doen zonder revisiecontrolesoftware. Dit maakt fusies onhandig, foutgevoelig en traag. Git-takken daarentegen zijn gewoon een verwijzing naar een commit. Omdat ze werken op het commit-niveau in plaats van direct op bestandsniveau, maken Git-branches het veel gemakkelijker om verschillende histories samen te voegen. Dit heeft een dramatische impact op vertakkingsworkflows.
Git scheidt filiaalfunctionaliteit in een paar verschillende commando's. De git branch
opdracht wordt gebruikt voor het weergeven, maken of verwijderen van branches.
Eerst en vooral moet u uw bestaande vestigingen kunnen bekijken:
git branch
Dit zal al je huidige takken uitvoeren, samen met een asterisk naast degene die momenteel is "uitgecheckt" (daarover later meer):
* beheer een quick-bug-fix voor bepaalde functies
De meester
branch is de standaardvertakking van Git, die is gemaakt met de eerste commit in een willekeurige repository. Veel ontwikkelaars gebruiken deze branche als de 'hoofd'-geschiedenis van het project - een permanente tak die elke belangrijke verandering bevat die deze doormaakt.
U kunt een nieuw filiaal maken door de filenaam hetzelfde door te geven git branch
commando:
git branch
Dit maakt een aanwijzer naar de stroom HOOFD
, maar doet niet overschakelen naar de nieuwe branch (je hebt het nodig git uitchecken
daarom). Meteen nadat u een nieuw filiaal heeft aangevraagd, ziet uw repository er ongeveer als volgt uit.
Uw huidige kantoor (meester
) en de nieuwe afdeling (sommige-functie
) verwijzen beide naar dezelfde commit, maar elke nieuwe commit die je opneemt zal exclusief zijn voor de huidige branch. Nogmaals, hiermee kunt u parallel aan niet-verwante functies werken, terwijl u nog steeds een verstandige geschiedenis bijhoudt. Bijvoorbeeld als uw huidige filiaal was sommige-functie
, je geschiedenis zou er als volgt uitzien nadat je een momentopname hebt gemaakt.
sommige-functie
tak De nieuwe HOOFD
(aangegeven door de gemarkeerde commit) bestaat alleen in de sommige-functie
tak. Het zal niet verschijnen in de loguitvoer van meester
, noch worden de wijzigingen in de werkdirectory weergegeven nadat u bent uitgecheckt meester
.
U kunt de nieuwe branch daadwerkelijk in de interne database zien door het bestand te openen .git / refs / heads /
. Het bestand bevat de ID van de verwezen commit en het is de enige definitie van een Git branch. Dit is de reden dat vestigingen zo licht zijn en gemakkelijk te beheren.
Ten slotte kunt u takken verwijderen via de -d
vlag:
git branch -d
Maar Git's toewijding om nooit je werk te verliezen, voorkomt dat het takken verwijdert met niet vastgemaakte commits. Om de verwijdering te forceren, gebruikt u de -D
vlag in plaats daarvan:
git branch -D
Onveranderde veranderingen zullen verloren gaan, dus wees erg voorzichtig met deze opdracht.
Het is natuurlijk nutteloos takken te maken zonder de mogelijkheid om tussen takken te schakelen. Git noemt dit het "uitchecken" van een filiaal:
git uitchecken
Na het uitchecken van de gespecificeerde vertakking, wordt uw werkdirectory bijgewerkt om overeen te komen met de commit van de gespecificeerde vertakking. tevens de HOOFD
wordt bijgewerkt om naar het nieuwe filiaal te wijzen, en alle nieuwe commits worden in het nieuwe filiaal opgeslagen. Je kunt denken aan het uitchecken van een filiaal als het overschakelen naar een nieuwe projectmap - behalve dat het veel gemakkelijker zal zijn om veranderingen terug te halen in het project.
Met dit in gedachten is het meestal een goed idee om een schoon werkmap voor het uitchecken van een filiaal. Een schone map bestaat wanneer er geen niet-gecommiteerde wijzigingen zijn. Als dit niet het geval is, git uitchecken
heeft het potentieel om uw wijzigingen te overschrijven.
Net als bij het uitvoeren van een "veilige" revisie, kunt u experimenteren met een nieuwe branche zonder de bestaande functionaliteit te hoeven vernietigen. Maar u hebt nu een speciale geschiedenis om mee te werken, zodat u de voortgang van een experiment kunt registreren met exact hetzelfde git add
en Git commit
opdrachten van eerder in het boek.
Deze functionaliteit zal nog krachtiger worden als we leren hoe we verschillende geschiedenissen kunnen samenvoegen in de "hoofd" tak (bijv., meester
). We komen daar zo meteen aan, maar eerst is er een belangrijke use-case van git uitchecken
dat moet worden overwogen ...
Git laat je ook gebruiken git uitchecken
met tags en commit-ID's, maar als je dit doet, word je in een onthechte HEAD staat. Dit betekent dat je niet meer op een filiaal bent - je bekijkt direct een commit.
Je kunt rondkijken en nieuwe commits toevoegen zoals gewoonlijk, maar aangezien er geen branch naar de toevoegingen verwijst, raak je al je werk kwijt zodra je terugkeert naar een echte branch. Gelukkig is het creëren van een nieuwe vestiging in een vrijstaand HOOFD
staat is gemakkelijk genoeg:
git checkout -b
Dit is een snelkoppeling voor git branch
gevolgd door git uitchecken
. Hierna krijgt u een glimmende nieuwe vertakkingreferentie naar de voorheen vrijstaande HOOFD
. Dit is een erg handige procedure om experimenten van oude revisies te onderzoeken.
Samenvoegen is het proces waarbij commits van de ene branch naar de andere worden getrokken. Er zijn veel manieren om filialen te combineren, maar het doel is altijd om informatie tussen filialen te delen. Dit maakt het samenvoegen van een van de belangrijkste kenmerken van Git. De twee meest voorkomende samenvoegmethodologieën zijn:
Ze gebruiken allebei hetzelfde commando, git fuseren
, maar de methode wordt automatisch bepaald op basis van de structuur van je geschiedenis. In ieder geval, de branch waar je in wilt fuseren, moet worden uitgecheckt, en de doelvertakking blijft ongewijzigd. De volgende twee secties presenteren twee mogelijke samenvoegscenario's voor de volgende opdrachten:
git checkout master git merge some-feature
Nogmaals, hiermee wordt het sommige-functie
vertakken naar de meester
tak, waardoor de voormalige onaangeroerd. Normaal gesproken voer je deze opdrachten uit nadat je een functie hebt voltooid en deze wilt integreren in het stabiele project.
Het eerste scenario ziet er als volgt uit:
We hebben een filiaal gemaakt om een nieuwe functie te ontwikkelen, twee commits toegevoegd en nu is het klaar om te worden geïntegreerd in de hoofdcodebasis. In plaats van het herschrijven van de twee commits die ontbreken meester
, Git kan het "snel vooruitspoelen" meester
de aanwijzer van de branche om overeen te komen met de locatie van sommige-functie
.
Na de samenvoeging, de meester
branch bevat alle van de gewenste geschiedenis, en de feature branch kan worden verwijderd (tenzij u zich wilt blijven ontwikkelen). Dit is het eenvoudigste type samenvoeging.
Natuurlijk hadden we de twee commits rechtstreeks op de meester
tak; Het gebruik van een speciale functietak gaf ons echter een veilige omgeving om met nieuwe code te experimenteren. Als het niet helemaal klopte, hadden we eenvoudigweg de branch kunnen verwijderen (in tegenstelling tot resetten / terugzetten). Of, als we een aantal tussenliggende commits met een gebroken code toevoegen, kunnen we deze opschonen voordat deze wordt samengevoegd meester
(zie Rebasing hieronder). Naarmate projecten ingewikkelder worden en meer medewerkers krijgen, maakt dit soort vertakte ontwikkeling Git tot een fantastische organisatorische tool.
Maar niet alle situaties zijn eenvoudig genoeg voor een fast-forward commit. Denk eraan, het belangrijkste voordeel van branches is het vermogen om tegelijkertijd vele onafhankelijke ontwikkelingslijnen te verkennen. Als gevolg hiervan kom je vaak een scenario tegen dat er als volgt uitziet:
Dit begon als een snelle fusie, maar we hebben een commit toegevoegd aan de meester
tak terwijl we nog aan het ontwikkelen waren sommige-functie
. We hadden bijvoorbeeld kunnen stoppen met werken aan de functie om een tijdgevoelige bug te repareren. Natuurlijk moet de bugfix zo snel mogelijk aan de hoofdrepository worden toegevoegd, dus we komen terecht in het scenario hierboven.
De functietak samenvoegen in meester
in deze context resulteert dit in een "3-weg" samenvoeging. Dit wordt bereikt door exact dezelfde commando's te gebruiken als het snel vooruitspoelen van de vorige sectie.
Git kan het bestand niet snel vooruitspoelen meester
aanwijzer naar sommige-functie
zonder backtracking. In plaats daarvan genereert het een nieuw voeg commit samen dat staat voor de gecombineerde snapshot van beide takken. Merk op dat deze nieuwe commit heeft twee ouder commits, waardoor het toegang heeft tot beide geschiedenissen (inderdaad, rennen git log
nadat de 3-weg samenvoegingsprogramma's van beide vestigingen zijn vastgelegd).
De naam van dit samenvoegalgoritme is afkomstig van de interne methode die is gebruikt om de merge commit te maken. Git kijkt naar drie commit om de uiteindelijke status van de samenvoeging te genereren.
Als u twee takken probeert te combineren die verschillende wijzigingen aanbrengen in hetzelfde gedeelte van de code, weet Git niet welke versie moet worden gebruikt. Dit wordt a genoemd conflict samenvoegen. Vanzelfsprekend kan dit nooit gebeuren tijdens een snel doorsturen. Wanneer Git een samenvoegingsconflict tegenkomt, ziet u het volgende bericht:
Index.html automatisch samenvoegen CONFLICT (inhoud): conflict samenvoegen inAutomatisch samenvoegen mislukt; repareer conflicten en voer vervolgens het resultaat uit.
In plaats van automatisch de samenvoeg commit toe te voegen, stopt Git en vraagt je wat je moet doen. hardlopen git status
in deze situatie zal iets als het volgende terugkeren:
# Op filiaal master # Onverharde paden: # # beide gemodificeerd:
Elk bestand met een conflict wordt opgeslagen onder het gedeelte 'Ongemande paden'. Git annoteert deze bestanden om u de inhoud van beide versies te tonen:
<<<<<<< HEAD This content is from the current branch. ======= This is a conflicting change from another branch. >>>>>>> een functie
Het gedeelte voor de =======
is van de meester
branch, en de rest is van de branch die je probeert te integreren.
Om het conflict op te lossen, verwijdert u het <<<<<<
, =======
, en >>>>>>>
notatie en verander de code in alles wat u wilt behouden. Vertel Git dan dat je klaar bent met het oplossen van het conflict met de git add
commando:
git add
Dat is juist; het enige wat je hoeft te doen is het conflicterende bestand te trainen om het als opgelost te markeren. Eindelijk, voltooi de drieweg samenvoeging door de samenvoeg commit te genereren:
Git commit
Het logbericht bevat een bericht over samenvoegingen, samen met een lijst met conflicten. Dit kan met name handig zijn wanneer u probeert te achterhalen waar iets fout is gegaan in een project..
En dat is alles wat er is om samen te voegen in Git. Nu we een goed inzicht hebben in de werking van Git-vestigingen, kunnen we een grondige blik werpen op hoe veteraan Git-gebruikers takken gebruiken in hun dagelijkse workflow.
De workflows die in dit gedeelte worden gepresenteerd, zijn het kenmerk van op Git gebaseerde revisieregeling. De lichtgewicht, eenvoudig samen te voegen aard van de branchemplementatie van Git maakt ze tot een van de meest productieve tools in uw software-ontwikkelingsarsenaal.
Alle vertakte workflows draaien rond de git branch
, git uitchecken
, en git fuseren
commando's gepresenteerd eerder dit hoofdstuk.
Het is vaak handig om een speciale betekenis aan verschillende takken toe te kennen omwille van het organiseren van een project. Dit gedeelte introduceert de meest voorkomende soorten branches, maar onthoud dat deze verschillen puur oppervlakkig zijn - voor Git is een branch een branch.
Alle takken kunnen als één van beide worden gecategoriseerd blijvend takken of onderwerp takken. De eerste bevatten de hoofdgeschiedenis van een project (bijv., meester
), terwijl de laatste tijdelijke takken zijn die worden gebruikt om een specifiek te implementeren onderwerp, vervolgens weggegooid (bijv., sommige-functie
).
Permanente vestigingen zijn de levensader van elke repository. Ze bevatten elk belangrijk waypoint van een softwareproject. De meeste ontwikkelaars gebruiken meester
uitsluitend voor stabiele code. In deze workflows, jij nooit commit direct op meester
-het is slechts een integratietak voor voltooide functies die werden gebouwd in speciale thematische takken.
Bovendien voegen veel gebruikers een tweede abstractielaag toe in een andere integratietak (gewoonlijk genoemd ontwikkelen
, hoewel elke naam voldoende is). Dit maakt de meester
filiaal voor werkelijk stabiele code (bijvoorbeeld openbare commits) en gebruiken ontwikkelen
als een interne integratietak om zich voor te bereiden op een openbare vrijgave. In het volgende diagram ziet u bijvoorbeeld verschillende functies die worden geïntegreerd ontwikkelen
, dan komt een enkele, laatste samenvoegen in meester
, wat een openbare versie symboliseert.
meester
filiaal exclusief voor openbare uitgaven Onderwerpstakken vallen over het algemeen in twee categorieën: kenmerk takken en hotfix-branches. Feature branches zijn tijdelijke branches die een nieuwe functie of refactor inkapselen en het hoofdproject beschermen tegen ongeteste code. Ze komen meestal voort uit een andere kenmerkentak of een integratietak, maar niet uit de "superstabiele" vertakking.
Hotfix-vertakkingen zijn vergelijkbaar van aard, maar ze komen voort uit de public-release-tak (bijv., meester
). In plaats van het ontwikkelen van nieuwe functies, zijn ze bedoeld om snel de hoofdlijn van ontwikkeling te patchen. Dit betekent meestal bugfixes en andere belangrijke updates die niet kunnen wachten tot de volgende grote release.
meester
met een hotfix-branch Nogmaals, de betekenissen die aan elk van deze takken zijn toegewezen, zijn puur conventioneel - Git ziet geen verschil tussen meester
, ontwikkelen
, functies en hotfixes. Met dat in gedachten, wees niet bang om ze aan te passen aan je eigen doelen. De schoonheid van Git is de flexibiliteit. Wanneer u de werking van Git-vestigingen begrijpt, is het eenvoudig om nieuwe workflows te ontwerpen die passen bij uw project en persoonlijkheid.
Rebasen is het verplaatsen van een filiaal naar een nieuw baseren. De rebaseringsmogelijkheden van Git maken filialen nog flexibeler doordat gebruikers hun filialen handmatig kunnen organiseren. Zoals samenvoegen, git rebase
vereist dat de branch wordt uitgecheckt en neemt de nieuwe basis als argument:
git checkout some-feature git rebase master
Dit verplaatst het geheel sommige-functie
vertakken op het puntje van meester
:
sommige-functie
op de meester
tak Na de rebase is de kenmerkentak een lineaire uitbreiding van meester
, wat een veel schonere manier is om veranderingen van de ene tak naar de andere te integreren. Vergelijk deze lineaire geschiedenis met een samenvoeging van meester
in sommige-functie
, wat resulteert in exact dezelfde codebasis in de uiteindelijke snapshot:
meester
in sommige-functie
met een driewegsamenvoeging Omdat de geschiedenis is gedivergeerd, moet Git een extra samenvoeg commit gebruiken om de branches te combineren. Dit vele malen doen tijdens het ontwikkelen van een langlopende functie kan resulteren in een erg rommelige geschiedenis.
Deze extra samenvoegcommissies zijn overbodig: ze bestaan alleen om wijzigingen van te verwijderen meester
in sommige-functie
. Meestal wilt u dat uw samenvoegingen worden vastgelegd gemiddelde iets, zoals de voltooiing van een nieuwe functie. Dit is de reden waarom veel ontwikkelaars ervoor kiezen om veranderingen door te voeren git rebase
, omdat het resulteert in een volledig lineaire geschiedenis in de functietak.
Interactief rebasen gaat een stap verder en stelt u in staat om verandering commit terwijl je ze naar de nieuwe basis verplaatst. U kunt een interactieve rebase opgeven met de -ik
vlag:
git rebase -i meester
Dit vult een teksteditor in met een samenvatting van elke commit in de functietak, samen met een opdracht die bepaalt hoe het moet worden overgebracht naar de nieuwe basis. Als u bijvoorbeeld twee commits op een functietak hebt, kunt u een interactieve rebase opgeven, zoals de volgende:
Kies 58dec2a Eerste commit voor nieuwe feature squash 6ac8a9f Tweede commit voor nieuwe feature
De standaard plukken
commando verplaatst de eerste commit naar de nieuwe base, net als de normale git rebase
, maar dan de squash
commando vertelt Git om de tweede commit te combineren met de vorige, dus je krijgt een commit met al je wijzigingen:
sommige-functie
tak Git biedt verschillende interactieve rebasingopdrachten, die allemaal samengevat zijn in het commentaargedeelte van de configuratielijst. Het punt is dat interactieve rebasing je in staat stelt helemaal herschrijf de geschiedenis van een filiaal naar uw exacte specificaties. Dit betekent dat je zoveel mogelijk tussenliggende commits aan een featuretak kunt toevoegen als je nodig hebt, en dan terug kunt gaan en ze kunt repareren tot een zinvolle progressie na het feit.
Andere ontwikkelaars zullen denken dat je een briljante programmeur bent, en wisten precies hoe ze de hele functie in één keer moesten implementeren. Dit soort organisatie is erg belangrijk om ervoor te zorgen dat grote projecten een navigeerbare geschiedenis hebben.
Rebasing is een krachtige tool, maar je moet wel oordeelkundig zijn in het herschrijven van de geschiedenis. Beide soorten rebasen niet echt verhuizing bestaande commits-zij creëren geheel nieuwe (aangegeven met een asterisk in het bovenstaande diagram). Als je commits inspecteert die werden onderworpen aan een rebase, zul je merken dat ze verschillende ID's hebben, ook al vertegenwoordigen ze dezelfde inhoud. Dit betekent rebasen vernietigt bestaande commits in het proces om ze te "verplaatsen".
Zoals je je misschien kunt voorstellen, heeft dit dramatische gevolgen voor collaboratieve workflows. Het vernietigen van een openbare commit (bijv. Alles op de meester
tak) is als het weghalen van de basis van het werk van iedereen. Git zal geen idee hebben hoe de veranderingen van iedereen moeten worden gecombineerd, en je zult je heel veel moeten verontschuldigen. We gaan dit scenario nader bekijken nadat we hebben geleerd hoe we moeten communiceren met externe opslagplaatsen.
Houd je voorlopig aan de gouden regel van rebasen: rebase nooit een branch die naar een openbare repository is geduwd.
Deze les vertegenwoordigt een hoofdstuk van Git Kortbij, een gratis eBoek van het team van Syncfusion.