Puține lucruri îmi satisfac mai mult decât o linie elegantă de Bash care automatizează ore de muncă plictisitoare.

Ca parte a unor explorări recente în recrearea automată a laptopului cu scripturi Bash (post to come!), Am vrut să găsesc o modalitate de a clona cu ușurință depozitele găzduite de GitHub pe o nouă mașină. După un pic de săpat în jur, am scris o linie care a făcut exact asta.

Apoi, în spiritul de a nu pune toate ouăle în același coș, am scris încă un singur liner pentru a crea și a împinge automat și copiile de rezervă găzduite de GitLab. Aici sunt ei.

Un singur liner Bash pentru a clona toate depozitele dvs. GitHub

Avertisment: veți avea nevoie de o listă a depozitelor GitHub pe care doriți să le clonați. Lucrul bun în acest sens este că vă oferă o agenție completă pentru a alege doar depozitele pe care le doriți pe mașină, în loc să mergeți în întregime.

Puteți clona cu ușurință depozitele GitHub fără a vă introduce parola de fiecare dată utilizând HTTPS cu Acreditări în cache de 15 minute sau, metoda mea preferată, de conectarea la GitHub cu SSH. Pentru scurtă durată, voi presupune că mergem cu acesta din urmă, iar cheile SSH sunt configurate.

A fost dată o listă de adrese URL GitHub din fișier gh-repos.txt, asa:

git@github.com:username/first-repository.git
git@github.com:username/second-repository.git
git@github.com:username/third-repository.git

Noi fugim:

xargs -n1 git clone < gh-repos.txt

Aceasta clonează toate depozitele din listă în folderul curent. Același singur liner funcționează și pentru GitLab, dacă înlocuiți adresele URL corespunzătoare.

Ce se petrece aici?

Există două jumătăți ale acestei linii: intrarea, contraintuitiv pe partea dreaptă și partea care face ca lucrurile să se întâmple, pe stânga. Am putea face ordinea acestor părți mai intuitivă (poate?) Scriind aceeași comandă astfel:

<gh-repos.txt xargs -n1 git clone 

Pentru a rula o comandă pentru fiecare linie a intrării noastre, gh-repos.txt, folosim xargs -n1. Unealta xargs citește elemente din intrare și execută orice comenzi găsite (va face echo dacă nu găsește niciunul). În mod implicit, presupune că articolele sunt separate prin spații; noi linii funcționează și fac lista noastră mai ușor de citit. Steagul -n1spune xargs a folosi 1 argument sau, în cazul nostru, o linie, pe comandă. Ne construim porunca cu git clone, care xargs apoi execută pentru fiecare linie. Ta-da.

Un Bash one-liner pentru a crea și împinge multe depozite pe GitLab

GitLab, spre deosebire de GitHub, ne permite să facem acest lucru ingenios în care nu trebuie să folosim site-ul pentru a crea mai întâi un nou depozit. Noi putem creați un nou depozit GitLab de la terminalul nostru. Depozitul nou creat implicit este setat ca Privat, așa că, dacă vrem să îl facem Public pe GitLab, va trebui să facem acest lucru manual mai târziu.

Documentele GitLab ne spun să împingem pentru a crea un nou proiect folosind git push --set-upstream, dar nu consider că acest lucru este foarte convenabil pentru utilizarea GitLab ca rezervă. În timp ce lucrez cu depozitele mele în viitor, aș dori să rulez o comandă care împinge atât GitHub și GitLab fără efort suplimentar din partea mea.

Pentru ca acest Bash să funcționeze într-o singură linie, vom avea nevoie, de asemenea, de o listă de adrese URL de depozitare pentru GitLab (cele care nu există încă). Putem face acest lucru cu ușurință copiind lista de depozite GitHub, deschizând-o cu Vim și făcând un căutați și înlocuiți:

cp gh-repos.txt gl-repos.txt
vim gl-repos.txt
:%s/<github>/gitlab/g
:wq

Aceasta produce gl-repos.txt, care arată ca:

git@gitlab.com:username/first-repository.git
git@gitlab.com:username/second-repository.git
git@gitlab.com:username/third-repository.git

Putem crea aceste depozite pe GitLab, putem adăuga adresele URL ca telecomenzi și ne putem împinge codul în noile depozite rulând:

awk -F'/|(.git)' '{system("cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push")}' gl-repos.txt

Stai bine și îți explic; pentru moment, ia act de faptul că ~/FULL/PATH/ ar trebui să fie calea completă către directorul care conține depozitele noastre GitHub.

Trebuie să notăm câteva ipoteze:

  1. Numele directorului de pe mașina dvs. locală care conține depozitul este același cu numele depozitarului din adresa URL (acesta va fi cazul dacă a fost clonat cu un singur liner de mai sus);
  2. Fiecare depozit este în prezent verificat la ramura pe care doriți să o împingeți, adică. master.

One-liner-ul ar putea fi extins pentru a face față acestor ipoteze, dar este părerea umilă a autorului că, în acel moment, ar trebui să scriem un script Bash.

Ce se petrece aici?

Bash-ul nostru Bash folosește fiecare linie (sau adresă URL) în gl-repos.txt fișier ca intrare. Cu awk, împarte numele directorului care conține depozitul pe mașina noastră locală și folosește aceste informații pentru a construi comanda noastră mai mare. Dacă am fi print rezultatul awk, am vedea:

cd ~/FULL/PATH/first-repository && git remote set-url origin --add git@gitlab.com:username/first-repository.git && git push
cd ~/FULL/PATH/second-repository && git remote set-url origin --add git@gitlab.com:username/second-repository.git && git push
cd ~/FULL/PATH/third-repository && git remote set-url origin --add git@gitlab.com:username/third-repository.git && git push

Să vedem cum construim această comandă.

Împărțirea corzilor cu awk

Unealta awk poate împărți intrarea pe baza separatoare de câmp. Separatorul implicit este un caracter de spațiu alb, dar putem schimba acest lucru trecând -F steag. Pe lângă personajele unice, putem folosi și un separator de câmp de expresie regulată. Deoarece adresele URL ale depozitului nostru au un format setat, putem apela numele depozitului solicitând subșirul dintre caracterul slash / și sfârșitul adresei URL, .git.

O modalitate de a realiza acest lucru este prin regex /|(.git):

  • / este un evadat / caracter;
  • | înseamnă „sau”, spunând awk să se potrivească oricărei expresii;
  • (.git) este grupul de captură de la sfârșitul adresei URL care se potrivește cu „.git”, cu un escape . caracter. Acesta este un pic de înșelătorie, deoarece „.git” nu împarte strict nimic (nu există nimic de cealaltă parte), dar este o modalitate ușoară pentru noi de a lua acest bit.

Odată ce am spus awk unde să ne împărțim, putem apuca șirul potrivit cu operator de teren. Ne referim la câmpurile noastre cu un $ caracter, apoi după numărul coloanei câmpului. În exemplul nostru, dorim al doilea câmp, $2. Iată cum arată toate șirurile secundare:

1: git@gitlab.com:username
2: first-repository

Pentru a utiliza întregul șir sau, în cazul nostru, întreaga adresă URL, folosim operatorul de câmp $0. Pentru a scrie comanda, doar înlocuim operatorii de câmp cu numele depozitului și adresa URL. Rularea asta cu print pe măsură ce construim, ne poate ajuta să ne asigurăm că avem toate spațiile corecte.

awk -F'/|(.git)' '{print "cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push"}' gl-repos.txt

Rularea comenzii

Ne construim porunca în parantezele lui system(). Utilizând acest lucru ca rezultat al awk, fiecare comandă va rula imediat ce este construită și ieșită. system() funcția creează un procesul copilului care execută comanda noastră, apoi revine odată ce comanda este finalizată. În engleză simplă, acest lucru ne permite să executăm comenzile Git pe fiecare depozit, una câte una, fără a ne rupe de procesul nostru principal în care awk face lucruri cu fișierul nostru de intrare. Iată din nou comanda noastră finală, toate împreună.

awk -F'/|(.git)' '{system("cd ~/FULL/PATH/" $2 " && git remote set-url origin --add " $0 " && git push")}' gl-repos.txt

Folosind copiile noastre de rezervă

Prin adăugarea adreselor URL GitLab ca telecomenzi, am simplificat procesul de împingere către ambele depozite găzduite extern. Dacă alergăm git remote -v într-unul din directoarele noastre de depozit, vom vedea:

origin  git@github.com:username/first-repository.git (fetch)
origin  git@github.com:username/first-repository.git (push)
origin  git@gitlab.com:username/first-repository.git (push)

Acum, pur și simplu aleargă git push fără argumente va împinge ramura curentă către ambele depozite la distanță.

De asemenea, ar trebui să menționăm că git pull în general va încerca doar să extragă din depozitul de la distanță din care ați clonat inițial (adresa URL marcată (fetch) în exemplul nostru de mai sus). Extragerea din mai multe depozite Git în același timp este posibilă, dar complicată și dincolo de scopul acestei postări. Iată un explicație de împingere și tragere la mai multe telecomenzi pentru a vă ajuta să începeți, dacă sunteți curios. Documentație Git pe telecomenzi poate fi de asemenea de ajutor.

Pentru a elabora succintitatea Bash one-liners

Bash one-liners, atunci când este înțeles, pot fi comenzi rapide distractive și la îndemână. Cel puțin, fiind conștient de instrumente precum xargs și awk poate ajuta la automatizarea și ameliorarea multă oboseală în munca noastră. Cu toate acestea, există unele dezavantaje.

În ceea ce privește un instrument ușor de înțeles, de întreținut și accesibil, Bash one-liners suge. De obicei, sunt mai complicate de scris decât folosind un script Bash if sau while bucle, și cu siguranță mai complicat de citit. Probabil că atunci când le scriem, vom rata un singur citat sau o paranteză de închidere undeva; și, așa cum sper să demonstreze această postare, pot să explice destul de mult. Deci, de ce să le folosești?

Imaginați-vă citind pas cu pas o rețetă pentru coacerea unei prăjituri. Înțelegeți metodele și ingredientele și vă adunați proviziile. Apoi, pe măsură ce vă gândiți la asta, începeți să vă dați seama că, dacă aruncați toate ingredientele la cuptor în exact ordinea corectă, un tort se va materializa instantaneu. Încercați și funcționează!

Ar fi destul de satisfăcător, nu-i așa?