Blog

10 nivåer av Git-alias: avancerat och bortom

JUN 17, 2024

I del ett av mitt blogginlägg i två delar tog jag med dig på en resa från de allra mest grundläggande grunderna, introducerade alias i Git, visade hur de kan ersätta långa och komplexa kommandon och gick även igenom några koncept som de flesta användare inte känner till, till exempel alias som skickar parametrar till själva Git och avancerad loggformatering med anpassade pretty formats.

Jan Krag

Jan has been with us since 2014 as a Continuous Improvement Agent. He is an accredited trainer for Docker and Github and is also a certified life coach. Jan has a truly diverse range of interests. He breeds oriental cats, builds and flies his own kites, collects Rubik’s cubes, and enjoys snowboarding. Wow!

Innan du går vidare rekommenderar jag starkt att du först läser del ett.

I del två introducerar jag ännu mer avancerade koncept och ger mig in på riktigt ”långsökta” användningsområden för Git-alias, inklusive några rent vansinniga exempel. Till sist presenterar jag några alternativ för att hantera samma behov.

Så, låt oss sätta igång.

Nivå 6: ! Icke-Git-kommandon

Mer valuta för pengarna.

Git-alias låter oss utöka vår verktygslåda med saker vi kan göra i Git-kontexten. Det här avsnittet, och större delen av de följande, utforskar vad vi kan göra när vi kliver utanför gränserna för Git-underkommandon.

”Bang”-funktionen förändrar vad Git-alias kan användas till, eftersom den låter oss anropa skalet och köra valfria skalkommandon på vårt system. Det gör vi helt enkelt genom att inleda aliasets expansion med ett utropstecken (eller ”bang” i Unix-/skalvärlden).

Låt oss börja med ett mycket enkelt exempel som tydligt visar konceptet och faktiskt är användbart.

På vissa plattformar levereras Git förinstallerat med två användbara GUI-verktyg: ”Git gui” för staging och commits samt ”Gitk” för att visa historiken. Men varför är git gui ett Git-underkommando, medan gitk inte är det? Ganska förvirrande, men enkelt att lösa med ett alias:

Och när vi ändå håller på har jag haft ett liknande problem. När min hjärna är djupt inne i ”Git”-läge skriver mina fingrar ofta ”Git” innan jag har bestämt vilket kommando jag ska använda. Sedan bestämmer jag mig plötsligt för att jag behöver lista filerna i mappen och skriver snabbt ll <enter> (mitt dagliga skalalias för ls -al), och plötsligt får jag:

Nu fungerar ”felskrivningen” git ll bara, men den leder till upptäckten av en mycket viktig begränsning med bang-funktionen, som kan vara både en välsignelse och en förbannelse, vilket visas här.

Så även om mitt alias git ll verkar fungera, kommer det alltid att visa innehållet i repots rotmapp, även om jag befinner mig i en undermapp.

Skalkommandot pwd printar den aktuella working-directoryn; det här aliaset ger en snabb genväg för att skriva ut sökvägen där mitt repo finns.

Visserligen skulle just det här problemet kunna lösas bättre med ett vanligt Git-alias, så att man slipper skapa en ny skalsession (det är dock fortfarande oklart vilket som körs snabbast).

Men jag har åtminstone ett mycket bra användningsområde för den här bieffekten. Ibland, när du av andra skäl har cd:at djupt ner i en undermapp, blir det riktigt irriterande att läsa Git-statusens utdata eftersom alla andra sökvägar skrivs ut relativt till din aktuella mapp. Då visas ändringar exempelvis så här:

Så låt oss utnyttja bieffekten med ”rotmappen” och lägga till alternativ till det vanliga st-aliaset:

De visar alltid statusutdata relativt till repots rot.

Git-1

(-s är bara för det mindre utförliga ”korta” utdataformatet.)

Observera att en liknande effekt kan uppnås med konfigurationsvariabeln status.relativePaths och funktionen -c som diskuteras i ”nivå 5” i den första delen av det här blogginlägget.

För att avsluta det här avsnittet, låt oss ha lite kul. Om jag omedvetet skriver Git före ll kan jag ibland också skriva Git innan jag bestämmer mig för exempelvis Git status, vilket leder till följande olyckliga resultat:

$ git git statusgit: 'git' is not a git command.Så min hjärna kom på idén att skapa:

Ja, det här gör att git git status fungerar, och tack vare aliasens rekursiva natur fungerar nu till och med git git git git git status … i teorin.

I praktiken är det mer ett bra skämt än en bra idé, eftersom det får två stora konsekvenser. Det första problemet, som vi såg ovan, är att kommandot nu körs från repots rotmapp, vilket kan skapa förvirring. Det andra problemet är att det förstör den ganska viktiga möjligheten att köra git help git to get , hjälpsidan för det faktiska Git-kommandot. I stället skulle det nu skriva ut det betydligt mindre användbara:

Nivå 7: Återanvända Git-alias

Bygg vidare på det som kom före … Använd det som kom före

I äldre versioner av Git, före 2.20, fick Git-alias inte hänvisa till andra Git-alias. Det ledde ofta till många liknande alias i konfigurationsfilen, som vi såg när vi diskuterade anpassade Git log-kommandon.

Den enda tillgängliga lösningen var att använda bang-funktionen, eftersom det visade sig fungera utmärkt att ha:

Det här anropar inte rekursivt ett befintligt alias, utan startar en ny shell-session som råkar använda Git med ett alias.

Lyckligtvis har rekursiva alias varit tillåtna sedan Git 2.20 (2018), och sedan 2.30 (Q1 2021) förstår och översätter även bash-completion (tab-completion) dem.

För en betydligt renare Git-konfiguration kan jag nu ha:

Nivå 8: Skapa pipelines för åtgärder

Kedja Unix-kommandon för mer kontroll (eller galenskap)

Jag nämnde tidigare att bang-funktionen förändrade spelplanen, men vi har bara skrapat på ytan. Nästa steg kommer när du inser att ett anrop till ett shell gör det möjligt att kedja samman flera kommandon.

Det enklaste sättet att använda detta är alias som utför flera separata saker efter varandra med hjälp av shellet och operatorn &&.

Obs: Aliaset ovan är radbrutet för läsbarhetens skull. I konfigurationen måste det ligga på en rad. Mer om detta senare.

Vi kan till och med använda befintliga miljövariabler eller definiera nya direkt i ett alias.

In case of fire

Jag skrev ut en kopia till vårt eget kontor, vilket ledde till en lång och humoristisk diskussion på Slack om varför detta inte skulle fungera och alla sätt det behövde förbättras på.

Slutresultatet blev följande pärla till alias, som tydligt visade hur man använder variabler:

Men den verkliga styrkan kommer när vi tar detta ett steg längre och börjar använda Unix-pipes för att skicka utdata från ett kommando till ett annat.

Först definierar jag ett snabbt remoteurl-alias för att enklare kunna hämta URL:en till min Git remote. Men tänk om jag klonade repot med ssh och har en URL i formatet "git@", när jag vill använda den för att hitta webbplatsen för repot?

Låt oss skicka utdatan från det första kommandot till sed, som söker och ersätter samt byter ut git@ mot https://.

För att avrunda detta lägger vi till ett alias som skickar dess utdata direkt till mitt urklipp med Mac-kommandot pbcopy. (I Windows kan vi göra samma sak med clip.)

På samma sätt kan vi använda andra shell-funktioner, som att omdirigera utdata till filer. Ibland vill jag skapa en .mailmap-fil i mina repos. Då kan du till exempel "mappa" vissa bidragsgivares gamla e-postadresser till deras nya, eller se till att olika stavningar av en användare slås ihop.

För att få en bra utgångspunkt för en mailmap behöver jag en lista över författare med deras namn och e-postadresser i standardformatet "Jan Krag <jan.krag@example.com>", ungefär som det jag kan få från git shortlog -sne, men utan den första sifferkolumnen.

För att underlätta detta tog jag fram följande alias:

[alias]mm   = "!git log  --format='%aN ' | sort -u"mmm  = "!git mm >> .mailmap"mmme = "!git mmm && code .mailmap"

Loggen skriver helt enkelt ut författaren och e-postadressen för varje commit och använder sedan växeln "unique" i shell-kommandot sort för att ta bort dubbletter och sortera utdatan. Det första aliaset skriver ut dem i konsolen, medan det andra omdirigerar utdatan och skapar eller lägger till i en befintlig .mailmap-fil.

Det tredje är en smidig version för när "jag har bråttom", som direkt öppnar den nya .mailmap-filen i min VSCode-editor. Här kombinerar vi alltså pipes, omdirigeringar och &&-funktionen i ett alias.

Nivå 9: Funktioner

Bash-funktioner är oslagbara!

Låt oss ta ytterligare ett steg framåt i vad vi kan göra. Nu när vi har det här shell-kontextet får vi tillgång till ännu en funktion: möjligheten att definiera funktioner. Varför skulle jag vilja göra det, kanske du undrar? Till viss del gör det komplexa alias lite renare, men den viktigaste fördelen är möjligheten att läsa kommandoradsargument och använda dem på ett mer kontrollerat sätt.

I vanliga alias kan du välja att lägga till vilka växlar och argument du vill efter aliaset, och de läggs till efter expansionen. Om vi till exempel återgår till mitt Git-alias slog från föregående inlägg går det utmärkt att använda det som git slog -3 eller git slog --all. Men tänk om jag vill skapa ett alias där jag behöver använda ett argument på flera ställen?

Ett mycket enkelt exempel är mitt alias för att ta bort en branch både lokalt och på remote:

Observera syntaxen. I mitt shell-kontext börjar jag med att definiera en funktion, f(), som kör satserna från inledande { till avslutande };.

Till sist anropar jag bara funktionen med dess funktionsnamn, f. Det fina är att vi nu kan skicka argument till funktionen, och dessa blir tillgängliga inom funktionens scope som ”positionsparametrar”, numrerade från ett och uppåt. ${1} hänvisar alltså helt enkelt till det första argumentet som användaren anger när funktionen anropas, och som du kan se i exemplet kan vi använda en parameter hur många gånger vi vill.

Låt oss titta på ett annat enkelt exempel:

[alias] # Easy add a GitHub repo as new remote ghremote= "!f(){ git remote add $1 https://github.com/$2.git; }; f"

I det här exemplet använder jag en funktion, inte för att återanvända ett argument, utan för att jag vill ta emot flera argument och använda dem på mycket specifika ställen direkt i aliaset.

Du kanske märker att det här exemplet använder positionsparametrarna utan klammerparenteser. Det är bara för att illustrera att enligt bash-standard behövs parenteserna endast för positionsparametrar över nio, så det är en smaksak.

Låt oss prova aliaset ghremote:

Nivå 10: Att gå för långt

Introduktion till formatering över flera rader.

Vi har tidigare sett några exempel på alias som blir mycket långa rader i din konfiguration och därför är nära nog ”oläsbara”. Det finns dock ett sätt att faktiskt skapa riktiga alias över flera rader. Lägg bara till ett backslash i slutet, eftersom det escaper radbrytningen.

Men det besvarar egentligen inte frågan: ”bör jag?”

Vid någon punkt når vi gränsen för vad som faktiskt är rimligt att göra i alias, och då bör du överväga några av de alternativa lösningar som jag kommer att presentera i ”Nivå 11”. Men det här är trots allt ett blogginlägg om alias, så låt oss tänja lite på gränserna, så får du göra din egen bedömning.

Låt oss inleda det här avsnittet med ett ganska komplext men ibland användbart exempel som jag inte kommer att förklara i detalj.

Att kombinera detta med symbolic-ref-tricket från aliaset swm för att hitta standardbranchen lämnas som en övning till dig, kära läsare.

Det finns inte mycket mer att förklara, så låt oss bara titta på några fler exempel som kan inspirera dig att prova att skriva egna alias.

”Jag vill bara öppna webbplatsen för det här repot.”

Men vad gör man om Git-remote använder ssh?

Låt oss se hur det används:

git-1

Vilka filer får mest kärlek?

Jag hittade ursprungligen detta i ett blogginlägg om .gitconfig av Michael Wales och justerade det något efter mina preferenser.

[alias]churn = !git -p log --all -M -C --name-only\      --format='format:' $@ \        | sort \        | grep -v '^$' \        | uniq -c \        | sort -r \        | awk 'BEGIN {print count,file} {print $1 , $2}'

Och används i git-katas-arkivet:

$ git churn42 README.md24 basic-commits/README.md21 basic-branching/README.md19 ignore/README.md19 basic-staging/README.md17 submodules/README.md17 3-way-merge/README.md16 configure-git/README.md15 Overview.md14 ff-merge/README.md

Varför behöver jag veta hur jag fortsätter?

Jag håller ofta Git-utbildningar, och varje gång vi pratar om att lösa merge-/rebase-konflikter har jag det tveksamma nöjet att introducera:

git merge --continuegit rebase --continuegit cherry-pick --continuegit revert --continue

Vid något tillfälle började jag undra: ”Varför, åh varför, behöver jag som användare ange ’vad’ jag fortsätter med när Git uppenbarligen vet att det är i ett merge-läge, ett rebase-läge och så vidare? Varför finns det inte bara ett continue-kommando som ’gör rätt sak’?”

Som jag minns det gjorde jag det vi brukade göra före ChatGPT: sökte på nätet och hittade ett bash-skript som gör detta. Det är förmodligen också den vettiga lösningen, som nämns i nästa kapitel, men som ett fan gjorde jag det som behövde göras och fick det att fungera som ett rent alias. Därför kan jag nu presentera denna monstrositet som verkligen förkroppsligar andan i att gå till överdrift:

Dela Git-alias

För att avsluta det här avsnittet ska jag dela med mig av mitt ”heliga graal”-alias, som jag utvecklade för omkring 10 år sedan genom hårt arbete och beslutsamhet.

Problemet jag ville lösa var att dela alias med kollegor, till exempel i Slack. Om jag vill ge ett snabbt alias till en mindre erfaren Git-användare blir det onödigt komplicerat att varje gång behöva förklara hur man hittar och redigerar den globala konfigurationsfilen, var i filen aliaskoden ska placeras och så vidare.

Det vore mycket smidigare att bara skicka rätt kommando, git config –global alias.foo "gör den här Git-saken", som de kan köra direkt. För enkla alias skriver jag bara detta utantill, men för ens lite mer komplexa alias med citattecken och kanske variabler är det inte helt enkelt att få rätt. Därför kom jag på idén att skapa aliaset exportalias. Jag hade ingen aning om hur svårt det skulle vara att få just detta rätt, och ett av framgångskriterierna längs vägen var att det skulle kunna exportera sig självt. Jag har inte testat det ordentligt med alla riktigt galna alias på flera rader, men för sådana skulle jag ändå alltid dela .gitconfig-utdraget.

Jag lämnade medvetet detta utan radbrytningar för att säkerställa att det gick att exportera.

Låt oss testa det på ett mer komplext alias från tidigare i det här inlägget – ett som inte bara innehåller specialtecken utan även escapade citattecken.

Nivå 11: Bonusrunda

Finns det en gräns för galenskapen?

I det här sista bonusavsnittet ska jag introducera några alternativa sätt att utöka Git-funktionaliteten – alternativ som kan visa sig vara vettigare än flerradiga alias som fyller en hel sida.

Egna körbara filer

Först kommer insikten att Git är klokt konstruerat: varje körbar fil i din sökväg som heter git-something blir ett git something-kommando.

Det innebär att några av våra längre alias i stället kan implementeras som enkla bash-skript. Varför spelar det roll, kanske du undrar? Delvis för att du slipper mycket av formateringskrånglet med att hålla dig inom ramarna för .gitconfig-filen, som att behöva escapea radbrytningar och hantera onödiga citattecken.

En annan fördel är att vi bättre kan skriva ”riktiga” skript med korrekt tolkning av argument, felhantering, hjälptext för användning och så vidare.

Git kräver faktiskt inte ens att du använder Bash. Du kan skriva dina nya Git-kommandon i vilket språk som helst, även om du kanske vill hålla dig till ett språk med ett bra Git-bibliotek, till exempel Python, Java eller Go-lang. 

Git-tilläggspaket

Om vi kan skriva egna Git-kommandon som binärfiler i vår sökväg kan vi också skriva och distribuera hela samlingar av dem. Det finns till och med open source-projekt som tillhandahåller den här typen av ”Git-tillägg”, antingen för specifika ändamål eller som allmänna samlingar av ”användbara” kommandon.

En av de största av dessa generella samlingar är https://github.com/tj/git-extras, som efter installation lägger till över 70 nya kommandon i din verktygslåda, däribland det självrefererande git extras, som listar dem alla. Det kan installeras med de flesta vanliga pakethanterare eller klonas manuellt om det inte är ett alternativ, till exempel:$sudo apt-get install git-extras

eller

Jag kommer inte att lista alla 70 kommandon, men här är några utvalda:

  • git-abort: Avbryt pågående Git-åtgärd.

  • git-alias: Definiera, sök efter och visa alias.

  • git-magic: Automatisera rutiner för add/commit/push.

  • git-repl: Git read-eval-print-loop.

  • git-standup: Se vad du gjorde under den senaste arbetsdagen.

Ett annat tilläggspaket som kan vara värt att titta på är Git Pastiche, som innehåller ett betydligt mindre urval av kommandon, kanske lite mer på låg nivå. Jag tycker dock att särskilt git stats och git activity är användbara ibland.

I det här sammanhanget kan det också vara värt att nämna att något vi ser som ett ”kärntillägg” till Git, som Git LFS, även kallat Git Large File Storage, också är byggt som ett tillägg med det här konceptet och skrivet i Go. Kärnkommandot Git lfs är bara en git-lfs-körbar fil som i sin tur ser till att omdirigera alla lfs-underkommandon till andra Go-program.

Hjälp

Avslutningsvis tar jag upp hjälp mycket kort. Om du verkligen vill satsa fullt ut på dina egna Git-kommandon, precis som ”git extras” och andra har gjort, kan du skapa egna hjälpsidor.

När du skriver git help mycustom kontrollerar Git om det finns en man-sida för mycustom på ditt system. Att beskriva hur du skapar man-sidor ligger utanför ramen för det här blogginlägget, men det är lätt att hitta bra resurser som tar upp ämnet.

För Git-alias kanske du har märkt att något som git help st helt enkelt skriver ut:

Det kan vara användbart eller inte, beroende på dina behov.

För grundläggande alias som detta, det vill säga sådana som inte anropar bash, kan du också använda formatet git st --help för att öppna den vanliga hjälpsidan för kommandot som aliaset avser, i det här fallet git status.

Erkännanden och ansvarsfriskrivning

Exemplen i det här inlägget kommer från min personliga samling efter mer än ett decennium av Git-användning. Vissa har ändrats för att visa det jag vill demonstrera i varje avsnitt.

När det gäller ursprunget till dessa alias har jag skrivit vissa själv för att lösa ett eget problem, medan andra har förmedlats till mig av kollegor, deltagare i mina Git-kurser och vänner. Vissa har jag helt enkelt hittat på nätet genom åren och antingen kopierat som de är eller anpassat för eget bruk. Generellt finns en gemenskapsanda kring att dela sådant här, så jag hoppas att detta omfattas av ”fair use”.

På senare tid har jag börjat lägga till kommentarer i min konfiguration för mycket komplexa alias som anger var jag hittade dem, men även då är det svårt att spåra ursprunget till de här kodexemplen eftersom andra gör samma sak som jag: lånar från någonstans och anpassar vid behov.

Om du hittar ett alias i det här blogginlägget där du anser dig vara den ursprungliga författaren, meddela mig gärna. Då anger jag dig som källa eller tar bort det på begäran.

Kom ihåg att läsa del ett av det här blogginlägget om du inte redan har gjort det!

  • Software development
  • DevOps
  • CI/CD

Subscribe to our newsletter