Close

Git Hooks

Git hooks zijn scripts die automatisch worden uitgevoerd wanneer een bepaalde gebeurtenis plaatsvindt in een Git-repository. Ze laten je het interne gedrag van Git aanpassen en activeren aanpasbare acties op belangrijke punten in de ontwikkelingscyclus.

Hooks die worden uitgevoerd tijdens het creatieproces van de commit

Git hooks worden vaak gebruikt om een commit-beleid te stimuleren, de projectomgeving te wijzigen afhankelijk van de status van de repository, en voor de implementatie van workflows voor continue integratie. Maar aangezien scripts oneindig aanpasbaar zijn, kun je Git hooks gebruiken om vrijwel elk aspect van je ontwikkelingsworkflow te automatiseren of te optimaliseren.

In dit artikel beginnen we met een conceptueel overzicht van hoe Git hooks werken. Vervolgens zullen we enkele van de meest populaire hooks voor gebruik in zowel lokale repository's als repository's op de server bekijken.


Conceptueel overzicht


Alle Git hooks zijn gewone scripts die Git uitvoert wanneer bepaalde gebeurtenissen zich voordoen in de repository. Hierdoor zijn ze zeer eenvoudig te installeren en te configureren.

Hooks kunnen zich in zowel lokale repository's als op de server bevinden, en ze worden alleen uitgevoerd als reactie op acties in die repository. Verderop in dit artikel zullen we de categorieën hooks concreet bekijken. De configuratie die in de rest van deze sectie wordt besproken, is van toepassing op zowel lokale hooks als hooks op de server.

Hooks installeren

Hooks bevinden zich in de map .git/hooks van elke Git-repository. Git vult deze map automatisch met voorbeeldscripts wanneer je een repository initialiseert. Als je een kijkje neemt in .git/hooks, zie je de volgende bestanden:

applypatch-msg.sample       pre-push.sample
commit-msg.sample           pre-rebase.sample
post-update.sample          prepare-commit-msg.sample
pre-applypatch.sample       update.sample
pre-commit.sample
Databases
gerelateerd materiaal

Een volledige Git-repository verplaatsen

Logo Bitbucket
Oplossing bekijken

Git leren met Bitbucket Cloud

Dit zijn de meeste beschikbare hooks, maar de extensie .sample zorgt ervoor dat ze niet standaard worden uitgevoerd. Om een hook te 'installeren' hoef je alleen maar de extensie .sample te verwijderen. Of als je een nieuw script helemaal opnieuw schrijft, kun je gewoon een nieuw bestand toevoegen dat overeenkomt met een van de bovenstaande bestandsnamen, zonder de extensie .sample .

Probeer bijvoorbeeld een eenvoudige prepare-commit-msg hook te installeren. Verwijder de extensie .sample van dit script, en voeg het volgende toe aan het bestand:

#!/bin/sh

echo "# Please include a useful commit message!" > $1

Hooks moeten uitvoerbaar zijn, dus het kan zijn dat je de bestandsrechten van het script moet wijzigen als je het helemaal opnieuw maakt. Om er bijvoorbeeld zeker van te zijn dat prepare-commit-msg uitvoerbaar is, voer je de volgende opdracht uit:

chmod +x prepare-commit-msg

Je zou nu dit bericht moeten zien in plaats van het standaard commit-bericht elke keer dat je git commit uitvoert. We zullen nader bekijken hoe dit werkelijk werkt in de sectie Commit-bericht voorbereiden. Laten we voorlopig gewoon genieten van het feit dat we enkele interne functies van Git kunnen aanpassen.

De ingebouwde voorbeeldscripts zijn erg nuttige referenties, omdat ze de parameters documenteren die aan elke hook worden doorgegeven (ze variëren van hook tot hook).

Scripttalen

De ingebouwde scripts zijn voornamelijk shell- en PERL-scripts, maar je kunt elke scripttaal gebruiken die je maar wilt, zolang die maar als een uitvoerbaar bestand kan worden uitgevoerd. De shebang-lijn (#! /bin/sh) definieert in elk script hoe je bestand geïnterpreteerd moet worden. Dus om een andere taal te gebruiken, hoef je alleen maar het pad van je interpreter aan te passen.

We kunnen bijvoorbeeld een uitvoerbaar Python-script schrijven in het bestand prepare-commit-msg in plaats van shell-opdrachten te gebruiken. De volgende hook doet hetzelfde als het shellscript in de vorige sectie.

#!/usr/bin/env python

import sys, os

commit_msg_filepath = sys.argv[1]
with open(commit_msg_filepath, 'w') as f:
    f.write("# Please include a useful commit message!")

Merk op hoe de eerste regel veranderde en naar de Python-interpreter wees. En in plaats van $1 te gebruiken om toegang te krijgen tot het eerste argument dat aan het script werd doorgegeven, gebruikten we sys.argv[1] (nogmaals, hierover zo dadelijk meer).

Dit is een zeer krachtige functie voor Git hooks, omdat je daarmee kunt werken in de taal die je het prettigst vindt.

Scope van Hooks

Hooks zijn lokaal in een willekeurige Git-repository en ze worden niet naar de nieuwe repository gekopieerd als je git clone uitvoert. En aangezien hooks lokaal zijn, kunnen ze worden gewijzigd door iedereen die toegang heeft tot de repository.

Dit heeft een belangrijke invloed bij het configureren van hooks voor een team van ontwikkelaars. Eerst moet je een manier vinden om ervoor te zorgen dat de hooks tussen je teamleden up-to-date blijven. Ten tweede kun je ontwikkelaars niet dwingen om commits te maken die er op een bepaalde manier uitzien. Je kunt ze alleen maar aanmoedigen om dat te doen.

Het onderhouden van hooks voor een team van ontwikkelaars kan nogal lastig zijn, omdat de map .git/hooks niet wordt gekloond met de rest van je project en deze ook niet onder versiebeheer staat. Een eenvoudige oplossing voor beide problemen is om je hooks op te slaan in de eigenlijke projectmap (boven de map .git ). Zo kun je ze bewerken zoals elk ander bestand met versiebeheer. Om de hook te installeren, kun je er ofwel een symlink naar aanmaken in .git/hooks, of je kunt deze gewoon kopiëren en in de map de.git/hooks plakken wanneer de hook wordt bijgewerkt.

Hooks die worden uitgevoerd tijdens het creatieproces van de commit

Als alternatief biedt Git ook een Sjabloonmap-mechanisme die het makkelijker maakt om hooks automatisch te installeren. Alle bestanden en mappen in deze sjabloonmap worden gekopieerd naar de map .git elke keer dat je git init of git clone gebruikt.

Alle hieronder beschreven lokale hooks kunnen worden gewijzigd of volledig worden verwijderd door de eigenaar van een repository. Het is geheel aan elk teamlid of ze al dan niet een hook gebruiken. Met dit in gedachten kun je Git hooks het beste zien als een handig hulpmiddel voor ontwikkelaars in plaats van een strikt gehandhaafd ontwikkelingsbeleid.

Dat gezegd hebbende, is het mogelijk om commits te weigeren die niet voldoen aan een bepaalde norm door gebruik te maken van hooks op de server. We zullen hier later in het artikel meer over zeggen.

Lokale hooks


Lokale hooks hebben alleen invloed op de repository waarin ze zich bevinden. Onthoud bij het lezen van deze sectie dat elke ontwikkelaar zijn eigen lokale voorkeuren kan aanpassen, dus je kunt ze niet gebruiken om een commit-beleid door te voeren. Ze kunnen het voor ontwikkelaars echter veel makkelijker maken om zich aan bepaalde richtlijnen te houden. In deze sectie bespreken we 6 van de handigste lokale hooks:

  • pre-commit
  • prepare-commit-msg
  • commit-msg
  • post-commit
  • post-checkout
  • pre-rebase

Met de eerste 4 hooks ben je aan de volledige commit-levenscyclus gekoppeld, en met de laatste 2 hooks kun je wat extra acties of veiligheidscontroles uitvoeren voor de respectievelijke opdrachten git checkout en git rebase.

Met alle pre-hooks kun je de actie die gaat plaatsvinden aanpassen, terwijl de post-hooks alleen worden gebruikt voor meldingen.

We zullen ook enkele handige technieken bekijken om hook-argumenten te parseren en informatie op te vragen over de repository met behulp van Git-opdrachten op een lager niveau.

Pre-Commit

Het pre-commit-script wordt elke keer uitgevoerd als je git commit uitvoert voordat Git de ontwikkelaar om een commit-bericht vraagt of een commit-object genereert. Je kunt deze hook gebruiken om de momentopname te bekijken die bijna gecommit wordt. Misschien wil je bijvoorbeeld een aantal geautomatiseerde tests uitvoeren om er zeker van te zijn dat de commit geen bestaande functionaliteit breekt.

Er worden geen argumenten doorgegeven aan het pre-commit-script, en als je stopt met een status die niet gelijk is aan nul, wordt de hele commit afgebroken. Laten we eens kijken naar een vereenvoudigde (en uitgebreidere) versie van de ingebouwde pre-commit hook. Dit script breekt de commit af als er fouten in de witruimte worden gevonden, zoals gedefinieerd door de opdracht git diff-index (volgende witruimte, regels met alleen witruimte en een spatie gevolgd door een tab in het eerste streepje van een regel worden standaard als fouten beschouwd).

#!/bin/sh

# Check if this is the initial commit
if git rev-parse --verify HEAD >/dev/null 2>&1
then
    echo "pre-commit: About to create a new commit..."
    against=HEAD
else
    echo "pre-commit: About to create the first commit..."
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# Use git diff-index to check for whitespace errors
echo "pre-commit: Testing for whitespace errors..."
if ! git diff-index --check --cached $against
then
    echo "pre-commit: Aborting commit due to whitespace errors"
    exit 1
else
    echo "pre-commit: No whitespace errors :)"
    exit 0
fi

Om git diff-index te kunnen gebruiken, moeten we uitzoeken met welke commit-referentie we de index vergelijken. Normaal gesproken is dit HEAD; HEAD bestaat echter niet bij het aanmaken van de initiële commit, dus onze eerste taak is om dit randgeval op te lossen. We doen dit met git rev-parse --verify, waarmee eenvoudig wordt gecontroleerd of het argument (HEAD) een geldige referentie is. Het gedeelte >/dev/null 2>&1 dempt elke uitvoer van git rev-parse. HEAD of een leeg commit-object wordt opgeslagen in de variabele against voor gebruik met git diff-index. De hash 4b825d... is een magische commit-ID die staat voor een lege commit.

De opdracht git diff-index --cached vergelijkt een commit met de index. Door voor de optie --check te kiezen, vragen we deze om ons te waarschuwen als de wijziging leidt tot fouten met betrekking tot witruimtes. Zo ja, dan breken we de commit af door een exit-status of 1 terug te geven, anders stoppen we met 0 en wordt de commit-workflow normaal voortgezet.

Dit is slechts een voorbeeld van de pre-commit hook. Het gebeurt dat bestaande Git-opdrachten worden gebruikt om tests uit te voeren op de wijzigingen die zijn geïntroduceerd door de voorgestelde commit, maar je kunt alles doen wat je wilt in pre-commit, inclusief het uitvoeren van andere scripts, het uitvoeren van een testsuite van derden, of het controleren van de codestijl met Lint.

Commit-bericht voorbereiden

De prepare-commit-msg hook is genoemd naar de pre-commit hook om de teksteditor te vullen met een commit-bericht. Dit is een goede plek om de automatisch gegenereerde commit-berichten voor geblokkeerde of samengevoegde commits te wijzigen.

Een tot drie argumenten worden doorgegeven aan het script prepare-commit-msg:

1. De naam van een tijdelijk bestand dat het bericht bevat. Je wijzigt het commit-bericht door dit bestand ter plaatse te wijzigen.

2. Het type commit. Dit kan een message (optie -m of -F), een template (-t optie), een merge (als de commit een merge commit is) of een squash (als de commit andere commits squasht) zijn.

3. De SHA1-hash van de relevante commit. Alleen gegeven als de optie -c, -C, or --amend werd gegeven.

Net als bij pre-commit wordt de commit afgebroken als je stopt met een status die niet gelijk is aan nul.

We hebben al een eenvoudig voorbeeld gezien waarin het commit-bericht is bewerkt, maar laten we eens kijken naar een nuttiger script. Bij het gebruik van een issuetracker is het gebruikelijk om elke issue in een aparte branch te behandelen. Als je het nummer van de issue vermeldt in de naam van de branch, kun je een prepare-commit-msg hook schrijven om dit automatisch op te nemen in elk commit-bericht in je branch.

#!/usr/bin/env python

import sys, os, re
from subprocess import check_output

# Collect the parameters
commit_msg_filepath = sys.argv[1]
if len(sys.argv) > 2:
    commit_type = sys.argv[2]
else:
    commit_type = ''
if len(sys.argv) > 3:
    commit_hash = sys.argv[3]
else:
    commit_hash = ''

print "prepare-commit-msg: File: %s\nType: %s\nHash: %s" % (commit_msg_filepath, commit_type, commit_hash)

# Figure out which branch we're on
branch = check_output(['git', 'symbolic-ref', '--short', 'HEAD']).strip()
print "prepare-commit-msg: On branch '%s'" % branch

# Populate the commit message with the issue #, if there is one
if branch.startswith('issue-'):
    print "prepare-commit-msg: Oh hey, it's an issue branch."
    result = re.match('issue-(.*)', branch)
    issue_number = result.group(1)

    with open(commit_msg_filepath, 'r+') as f:
        content = f.read()
        f.seek(0, 0)
        f.write("ISSUE-%s %s" % (issue_number, content))

Ten eerste laat de bovenstaande prepare-commit-msg hook zien hoe je alle parameters verzamelt die aan het script worden doorgegeven. Vervolgens wordt git symbolic-ref --short HEAD opgeroepen om de branchnaam te krijgen die overeenkomt met HEAD. Als deze branchnaam begint met issue-, dan wordt de inhoud van het commit-berichtbestand herschreven zodat het nummer van de issue in de eerste regel wordt opgenomen. Dus als de naam van je branch issue-224 is, wordt het volgende commit-bericht gegenereerd.

ISSUE-224 

# Please enter the commit message for your changes. Lines starting 
# with '#' will be ignored, and an empty message aborts the commit. 
# On branch issue-224 
# Changes to be committed: 
#   modified:   test.txt

Iets om in gedachten te houden als je prepare-commit-msg gebruikt, is dat het zelfs werkt als de gebruiker een bericht invoert met de optie -m van git commit. Dit betekent dat het bovenstaande script automatisch de string ISSUE-[#] invoegt zonder dat de gebruiker deze kan bewerken. Je kunt dit geval verwerken door te kijken of de tweede parameter (commit_type) gelijk is aan message.

Maar zonder de optie -m kan de gebruiker met de prepare-commit-msg hook het bericht bewerken nadat het is gegenereerd, dus dit is eigenlijk meer een gemaksscript dan een manier om een beleid voor gecommitteerde berichten door te voeren. Daarvoor heb je de commit-msg hook nodig die in de volgende sectie wordt besproken.

Commitbericht

De commit-msg hook lijkt veel op de prepare-commit-msg hook, maar wordt aangeroepen nadat de gebruiker een commit-bericht heeft ingevoerd. Dit is een geschikte plaats om ontwikkelaars te waarschuwen dat hun boodschap niet voldoet aan de normen van je team.

Het enige argument dat aan deze hook wordt doorgegeven, is de naam van het bestand dat het bericht bevat. Als de gebruiker het bericht dat de gebruiker heeft ingevoerd niet goed vindt, kan het dit bestand ter plaatse wijzigen (net als bij prepare-commit-msg) of het kan de commit volledig afbreken door af te sluiten met een status die niet gelijk is aan nul.

Het volgende script wordt bijvoorbeeld gecontroleerd om er zeker van te zijn dat de gebruiker de string ISSUE-[#], die automatisch werd gegenereerd door de prepare-commit-msg hook in de vorige sectie, niet heeft verwijderd.

#!/usr/bin/env python

import sys, os, re
from subprocess import check_output

# Collect the parameters
commit_msg_filepath = sys.argv[1]

# Figure out which branch we're on
branch = check_output(['git', 'symbolic-ref', '--short', 'HEAD']).strip()
print "commit-msg: On branch '%s'" % branch

# Check the commit message if we're on an issue branch
if branch.startswith('issue-'):
    print "commit-msg: Oh hey, it's an issue branch."
    result = re.match('issue-(.*)', branch)
    issue_number = result.group(1)
    required_message = "ISSUE-%s" % issue_number

    with open(commit_msg_filepath, 'r') as f:
        content = f.read()
        if not content.startswith(required_message):
            print "commit-msg: ERROR! The commit message must start with '%s'" % required_message
            sys.exit(1)

Hoewel dit script elke keer wordt aangeroepen als de gebruiker een commit maakt, moet je proberen om dit niet te vaak te doen buiten de controle van commit-berichten. Als je andere services moet laten weten dat er een momentopname is gemaakt, moet je in plaats daarvan de post-commit hook gebruiken.

Post-Commit

De post-commit hook wordt onmiddellijk na de commit-msg hook aangeroepen. Het kan de uitkomst van de handeling git commit niet veranderen, dus wordt het voornamelijk gebruikt voor meldingsdoeleinden.

Het script gebruikt geen parameters en de exit-status heeft geen enkele invloed op de commit. Voor de meeste scripts post-commit heb je toegang nodig tot de commit die zojuist is aangemaakt. Je kunt git rev-parse HEAD gebruiken om de SHA1-hash van de nieuwe commit op te halen, of je kunt git log -1 HEAD gebruiken om alle informatie te verkrijgen.

Als je bijvoorbeeld elke keer dat je een momentopname maakt naar je baas een e-mail wilt sturen (waarschijnlijk niet het beste idee voor de meeste workflows), kun je de volgende post-commit hook toevoegen.

#!/usr/bin/env python

import smtplib
from email.mime.text import MIMEText
from subprocess import check_output

# Get the git log --stat entry of the new commit
log = check_output(['git', 'log', '-1', '--stat', 'HEAD'])

# Create a plaintext email message
msg = MIMEText("Look, I'm actually doing some work:\n\n%s" % log)

msg['Subject'] = 'Git post-commit hook notification'
msg['From'] = 'mary@example.com'
msg['To'] = 'boss@example.com'

# Send the message
SMTP_SERVER = 'smtp.example.com'
SMTP_PORT = 587

session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo()
session.login(msg['From'], 'secretPassword')

session.sendmail(msg['From'], msg['To'], msg.as_string())
session.quit()

Het is mogelijk om post-commit te gebruiken om een lokaal systeem voor continue integratie te activeren, maar meestal wil je dit doen in de post-receive hook. Deze draait op de server in plaats van op de lokale computer van de gebruiker, en draait ook telkens wanneer elke ontwikkelaar zijn code pusht. Hierdoor is het een veel geschiktere plek om je continue integratie uit te voeren.

Post-checkout

De post-checkout hook werkt ongeveer zoals de post-commit hook, maar wordt aangeroepen als je met succes een referentie bekijk met git checkout. Dit is handig om je werkmap met gegenereerde bestanden te legen die anders voor verwarring zouden zorgen.

Deze hook accepteert drie parameters en de exit-status heeft geen invloed op de opdracht git checkout.

1. De ref van de vorige HEAD

2. De ref van de nieuwe HEAD

3. Een markering die aangeeft of het een checkout van een branch of bestand was. De markering zal respectievelijk 1 en 0 zijn.

Een veelvoorkomend probleem bij Python-ontwikkelaars doet zich voor wanneer gegenereerde .pyc-bestanden blijven hangen na het wisselen van branch. De interpreter gebruikt soms deze .pyc in plaats van het .py -bronbestand. Om verwarring te voorkomen, kun je alle .pyc-bestanden verwijderen elke keer dat je een nieuwe branch bekijkt met behulp van het volgende post-checkout-script:

#!/usr/bin/env python

import sys, os, re
from subprocess import check_output

# Collect the parameters
previous_head = sys.argv[1]
new_head = sys.argv[2]
is_branch_checkout = sys.argv[3]

if is_branch_checkout == "0":
    print "post-checkout: This is a file checkout. Nothing to do."
    sys.exit(0)

print "post-checkout: Deleting all '.pyc' files in working directory"
for root, dirs, files in os.walk('.'):
    for filename in files:
        ext = os.path.splitext(filename)[1]
        if ext == '.pyc':
            os.unlink(os.path.join(root, filename))

De huidige werkmap voor hook-scripts is altijd ingesteld op de hoofdmap van de repository, dus de aanroep os.walk ('.') wordt herhaald door elk bestand in de repository. Vervolgens controleren we de extensie en verwijderen we die als deze een .pyc-bestand is.

Je kunt ook de post-checkout hook gebruiken om je werkmap aan te passen op basis van welke branch je bekijkt. Je zou bijvoorbeeld een plugins-branch kunnen gebruiken om al je plug-ins buiten de kern van de codebase op te slaan. Als er voor deze plug-ins veel binaire bestanden nodig zijn, wat andere branches niet hebben, kun je ze alleen selectief bouwen als je op de plugin-branch bent.

Pre-Rebase

De pre-rebase hook wordt aangeroepen voordat git rebase iets verandert, waardoor het een goede plek is om ervoor te zorgen dat er niets verschrikkelijks gaat gebeuren.

Voor deze hook zijn twee parameters nodig: de upstream-branch van waaruit de serie vertakt is, en de branch die wordt gerebased. De tweede parameter is leeg als je de huidige branch opnieuw rebaset. Om de rebase af te breken, moet je afsluiten met een status die niet gelijk is aan nul.

Als je bijvoorbeeld rebasen helemaal niet wilt toestaan in je repository, kun je het volgende pre-rebase-script gebruiken:

#!/bin/sh

# Disallow all rebasing
echo "pre-rebase: Rebasing is dangerous. Don't do it."
exit 1

Nu krijg je elke keer dat je git rebase start, dit bericht te zien:

pre-rebase: Rebasing is dangerous. Don't do it.
The pre-rebase hook refused to rebase.

Kijk voor een meer diepgaand voorbeeld naar het bijgevoegde script pre-rebase.sample. Dit script is iets intelligenter als het gaat om het niet toestaan van rebasen. Het controleert of de topic-branch die je probeert te rebasen al is samengevoegd met de next branch (waarvan wordt aangenomen dat het de main-branch is). Als dat zo is, kom je waarschijnlijk in de problemen door het te rebasen; daarom breekt het script de rebase af.

Hooks aan de serverzijde


Hooks op de server werken net als lokale hooks, behalve dat ze zich bevinden in repository's op de server (bijvoorbeeld een centrale repository of een openbare repository voor ontwikkelaars). Wanneer ze zijn gekoppeld aan de officiële repository, kunnen sommigen van deze dienen als een manier om het beleid te handhaven door bepaalde commits af te wijzen.

Er zijn 3 hooks op de server die we in de rest van dit artikel zullen bespreken:

  • pre-receive
  • Update
  • post-receive

Met al deze hooks kun je reageren op verschillende fasen van het git push-proces.

De output van hooks op de server wordt doorgeleid naar de console van de klant. Het is dus heel eenvoudig om berichten terug te sturen naar de ontwikkelaar. Maar je moet er ook rekening mee houden dat deze scripts de controle over de terminal pas teruggeven als ze klaar zijn met de uitvoering ervan. Wees dus voorzichtig met het uitvoeren van langlopende operaties.

Pre-Receive

De pre-receive hook wordt elke keer uitgevoerd als iemand git push gebruikt om commits naar de repository te pushen. Deze moet zich altijd in de externe repository bevinden die de bestemming is van de push, niet in de repository van herkomst.

De hook wordt uitgevoerd voordat er referenties worden bijgewerkt. Het is dus een goede plek om beleidsregels met betrekking tot ontwikkeling door te voeren. Als je niet blij bent met degene die pusht, hoe het commit-bericht wordt opgesteld of de wijzigingen in de commit, dan kun je het gewoon weigeren. Hoewel je ontwikkelaars er niet van kunt weerhouden om misvormde commits te maken, kun je voorkomen dat deze commits in de officiële codebase terechtkomen door ze af te wijzen met pre-receive.

Het script bevat geen parameters, maar elke ref die wordt gepusht, wordt op een aparte regel op standaardinput naar het script gestuurd in de volgende indeling:

<old-value> <new-value> <ref-name>

Je kunt zien hoe deze hook werkt door gebruik te maken van een heel eenvoudig pre-receive script dat eenvoudig de gepushte refs leest en deze print.

#!/usr/bin/env python

import sys
import fileinput

# Read in each ref that the user is trying to update
for line in fileinput.input():
    print "pre-receive: Trying to push ref: %s" % line

# Abort the push
# sys.exit(1)

Nogmaals, dit is een beetje anders dan de andere hooks, omdat er informatie aan het script wordt doorgegeven via standaardoutput in plaats van als opdrachtregelargumenten. Na het plaatsen van het bovenstaande script in de map .git/hooks van een externe repository en het pushen van de main-branch, zie je zoiets als het volgende in je console:

b6b36c697eb2d24302f89aa22d9170dfe609855b 85baa88c22b52ddd24d71f05db31f4e46d579095 refs/heads/main

Je kunt deze SHA1-hashes gebruiken, samen met enkele Git-opdrachten van een lager niveau, om te controleren welke wijzigingen zullen worden geïntroduceerd. Een paar veelgebruikte usecases zijn:

  • Afwijzen van wijzigingen die een upstream rebase hebben
  • Voorkomen van niet-fast-forward merges
  • Controleren of de gebruiker over de juiste rechten beschikt om de beoogde wijzigingen aan te brengen (meestal gebruikt voor gecentraliseerde Git-workflows)

Als meerdere refs worden gepusht, worden ze allemaal afgebroken als ze terugkeren naar een status die niet gelijk is aan nul van pre-receive. Als je branches per geval wilt accepteren of afwijzen, moet je in plaats daarvan de hook update gebruiken.

Bijwerken

De update hook wordt aangeroepen na de pre-receive en werkt ongeveer hetzelfde. Deze wordt nog steeds aangeroepen voordat er iets wordt bijgewerkt, maar wordt apart aangeroepen voor elke ref die gepusht is. Dat betekent dat als de gebruiker 4 branches probeert te pushen, de update 4 keer wordt uitgevoerd. In tegenstelling tot pre-receive hoeft deze hook niet uit de standaardinput te worden gelezen. In plaats daarvan accepteert deze de volgende 3 argumenten:

1. De naam van de ref die wordt bijgewerkt

2. De naam van het oude object die is opgeslagen in de ref

3. De naam van het nieuwe object die is opgeslagen in de ref

Dit is dezelfde informatie die wordt doorgegeven aan pre-receive, maar aangezien de update voor elke ref apart wordt aangeroepen, kun je sommige refs weigeren en andere toestaan.

#!/usr/bin/env python

import sys

branch = sys.argv[1]
old_commit = sys.argv[2]
new_commit = sys.argv[3]

print "Moving '%s' from %s to %s" % (branch, old_commit, new_commit)

# Abort pushing only this branch
# sys.exit(1)

De bovenstaande update hook geeft gewoon de branch en de oude/nieuwe commit-hashes weer. Als je meer dan één branch naar de externe repository pusht, wordt de statement print voor elke branch uitgevoerd.

Post-Receive

De post-receive hook wordt aangeroepen na een succesvolle push-operatie, waardoor het een goede plek is om meldingen uit te voeren. Voor veel workflows is dit een betere plek om meldingen te activeren dan de post-commit, omdat de wijzigingen beschikbaar zijn op een openbare server in plaats van alleen op de lokale computer van de gebruiker. Het sturen van e-mails naar andere ontwikkelaars en het activeren van een systeem voor continue integratie zijn veelvoorkomende usecases voor post-receive.

Het script gebruikt geen parameters, maar wordt via standaardinput dezelfde informatie verzonden als bij pre-receive.

Samenvatting


In dit artikel hebben we geleerd hoe Git hooks kunnen worden gebruikt om intern gedrag te veranderen en om meldingen te ontvangen wanneer bepaalde gebeurtenissen zich voordoen in een repository. Hooks zijn gewone scripts die zich in de repository .git/hooks bevinden, waardoor ze heel eenvoudig te installeren en aan te passen zijn.

We hebben ook gekeken naar enkele van de meest voorkomende hooks aan de lokale kant en op de server. Hiermee kunnen we een bijdrage leveren aan de volledige ontwikkelingscyclus. We weten nu hoe we aanpasbare acties kunnen uitvoeren in elke fase van het proces voor het aanmaken van commits en in het git push-proces. Met een beetje kennis van scripts kun je zo goed als alles doen wat je maar kunt bedenken met een Git-repository.


Deel dit artikel
Volgend onderwerp

Aanbevolen artikelen

Bookmark deze resources voor meer informatie over soorten DevOps-teams of voor voortdurende updates over DevOps bij Atlassian.

Mensen die samenwerken met een muur vol tools

Bitbucket-blog

Toelichting DevOps

DevOps-leertraject

Demo Den Feature-demo's met Atlassian-experts

Hoe Bitbucket Cloud werkt met Atlassian Open DevOps

Meld je aan voor onze DevOps-nieuwsbrief

Thank you for signing up