Versionskontrol med afhængige Git-projekter

Jeg har altid rodet meget med software versionskontrolsystemer, og for tiden er det Git jeg bruger tid på.
En af de ting med Git jeg ikke har set en god løsning på er håndtering af afhængigheder mellem Git-projekter.
Derfor vil jeg gerne høre jeres erfaring.

Illustration: Privatfoto

Ofte laver jeg forskellige programmerings-projekter, som afhænger af de samme grundfunktioner. Jeg nægter at starte de enkelte projekter med at kopiere grundfunktionerne ind.
Det er mere oplagt at strukturere min kode, så grundfunktioner samles i et eller flere Git-repositories, som så skal checkes ud sammen de egentlige projekter. Det giver langt bedre kontrol
med koden, og skal jeg tilføje flere grundfunktioner eller rette fejl, så er der kun et sted jeg gør det.

Min lomme-research (ca på niveau med den fineste forskning på området host) viser at der er mindst fire forskellige måder at håndtere dette på

Det jeg har set er, at Git submodule er "ok", men ifølge dokumentationen er der flere begrænsninger der:

  • Afhænger Git-projektet Top.git af Sub.git så bliver den version af Sub,git man importerer (med git submodule add) ind også reelt den man fortsætter med. Jeg kan opgradere, men det er ikke "bare lige".
    Egentlig kan jeg godt lide dette, men jeg vil gerne fra start bestemmer om underprojekter skal opdateres løbende mod en master-branch i underprojektets git-repository eller ej.
  • Jeg kan vist ikke klone mine hoved projekter f.eks. "Top.git" og automatisk få "Sub.git" ind. Det kræver at jeg efterfølgende kører "git submodule init" og "git submodule update". Det er lidt kluntet.
  • "Sub.git" skal altid checkes ud inde i mine projekter. Det vil i nogle sammenhænge være smart hvis jeg f.eks. kunne checke "Sub.git" ud parallelt med "Top.git".

Git subtree og Git-Repo har jeg ikke nået et se på endnu :-)

Jeg vil meget gerne høre jeres erfaringer med dem alle, og også om jeg har overset noget her.

/pto

Kommentarer (27)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Kræn Hansen

Hej Peter,

Du kan bruge en kommando som "git submodule foreach --recursive" til at køre kommandoer i hvert submodule. Det kan eksempelvis bruges til at pull'e alle submodules til nyeste version af deres respektive remote heads: "git submodule foreach --recursive git pull".
Så jeg vil mene at det er nogenlunde ligetil at opgradere submodules.

Som Esben skriver kan "git clone --recursive" bruges til at clone alle submodules samtidig med dit top-most repository.

Jeg tror ikke helt jeg forstår brugs-scenariet for din 3. anke?

  • 1
  • 0
Jacob Larsen

Din anke nr 1 håndteres meget godt af git-repo. Der kan du selv bestemme om det er en branch, et tag eller et specifikt commit du tracker for de enkelte projekter.

Jeg kan i øvrigt også godt lide at selve manifestet der binder de forskellige repositories sammen bliver tracket i sit eget repository.

  • 1
  • 0
Kristian Mandrup

Nu har jeg brugt git i en hel del år både til Ruby/Rails udvikling og senest til Node.js.
Jeg har aldrig oplevet at skulle have brug for submodules. Det er "so 90s" i min optik.
Man laver da simpelthen bare små komponenter (gems i Ruby, packages i Node.js) og så definerer man sine dependencies i en dependency fil (fx Gemfile for Ruby eller Packages.json i Node.js) og så kører man sin dependency manager... Hvilke sprog har endnu ikke denne tilgang til udvikling? Selv i et oldnordisk/primitivt sprog som Java er der Maven til det samme.

Ja, det gælder om at holde sine repositories små og dele det op i mindre bidder, et projekt for hver. Det eneste problem ved denne tilgang er, at man dermed ender med mange projekter (har vist selv mindst 400!) så hvis de alle skal være protected kan det give "issues" (så skal man til at tænke i at hoste sit eget git repo, ellers måske for dyrt med fx github).

  • 9
  • 0
Nikolaj Brinch Jørgensen

Jeg er ikke sikker på jeg ville anvende Git (eller Mercurial, Bazaar eller SCM overhovedet til den slags).
Det er som jeg læser det, dependency management, og klares af dit build tool (Gradle, Ivy, Maven osv. osv., der er også teknologi som OSGi til den slags modulopbygning) Det der er væsenligt er jo at kunne styre disse submodules versioneret, om det er kode, konfiguration eller binære afhængigheder.

  • 3
  • 0
Klavs Klavsen

Enig mht. at lade pakkehåndteringen håndtere det.
Hvis man godt vil kunne have flere versioner af samme afhængigheder på samme server - kan det være man bare skal bruge sådan noget som nævnt (gradle, maven mv.) og pakke det med ind som en del af ens pakke.

Ellers ville jeg benytte dit operativsystems primære pakkesystem til at sørge for at installere de afhængigheder du har - således de også kan være delt, imellem evt. andre ting, der har samme afhængighed.

altså RPM til rpm-distro'er og DEB til debian baserede - og lade dem håndtere dependencies mv. På den måde, kan du installere det direkte på den server det skal køre på - og have alle filer, styret af ét og samme pakkesystem - istedet for at benytte flere pakkesystemer på samme server, hvilket jeg mener er et problem.

  • 2
  • 2
Henrik Mikael Kristensen

Jeg tror det her ligger på et lavere niveau end RPM og DEB, nemlig de underprojekter og biblioteker, et enkelt program består af, og hvor alle delene af source koden skal samles i de rette versioner, ved build-time.

Jeg sidder selv i samme problematik, og overvejer hvilken vej, der skal tages, og vil gerne sige tak for tråden. :-)

  • 0
  • 0
Sune Marcher

Forstår ikke hvorvor Klavs' indlæg har fået downvotes - det må være fra folk der kun har erfaring med én platform?

Hvis der er nogen der har fundet den hellige gral der virker på tværs af alle sprog og platforme, vil jeg meget gerne høre om det - det er lidt synd hvis de kun kan sidde og føle sig selvfede og give downvotes.

Sådan som jeg ser verden, er der ret meget forskel på hvilken platform du bruger (hvor "platform" her dækker over en ikke helt simpel kombination af OS, programmeringssprog (plural), samt closed- vs. open-source).

Til Java-stuff er Maven næsten en no-brainer, det er langt fra perfekt men har ret god dependency management, og kan håndtere både opensource og private dependencies - og det er nogenlunde overskueligt at wrappe ikke-POM projekter og smide på en privat Nexus. Done.

Andre systemer har andre dependency-management muligheder, om det så er i stil med NPM og Gems, eller hvad man ser til open-source C/C++ (og andet) på Linux, hvor operativsystemets pakke-manager fungerer ganske udmærket, så længe man ikke har for specifikke versionkrav til de libraries man bruger (jeg har personligt set mere "DLL hell" på Linux end Windows, nok affødt af tendensen på Windows til at linke statisk (eller linke dynamisk og smide DLL'er i program-folder), hvor tendensen på Linux er at linke dynamisk mod shared system-folder... YMMV).

Men når man sidder med closed-source stuff, eller benytter bleeding-edge (eller bare ret specialiseret) kode man ikke kan forvente der er i et officielt repository (hvad enten det er RPM/DEB/Pac/Portage, NPM, PEAR, Gem, PPM, ...) er der lidt andre krav.

Jeg har ikke selv fundet nogen silver bullet - både Git submodules og subversion svn:external har en bunke issues, og nogle gange (C/C++) vælger jeg at lave local build og install af libraries, smide på include-path, og inkludere dem på den måde.

Ville have skrevet noget om at det ville være lækkert hvis folk ville prøve at være lidt løsnings-orienterede i stedet for at pege fingre, men har vist selv lige jokket i præcis den samme spinat. Software development is messy :-)

  • 2
  • 1
Peter Müller
  • 1
  • 0
Klavs Klavsen

harddisk plads er billig, og det kan sagtens give mening at pakke afhængigheder med IND i koden - det kan man ligeså vel gøre med npm som med rpm. Det gør ingen forskel. RPM er så platformsafhængig - hvor npm er supporteret på andet end linux platforme (formodentlig).

Til gengæld har man problemet med flere pakkesystemer der ikke kender til hinandens filer.

  • 2
  • 0
Pauli Østerø

Det gør at du komplet eliminerer "DLL hell" fordi to forskellige versioner af den samme pakke kan sameksistere uden problemer. Npm er ubetinget den bedste package manager jeg har stiftet bekendtskab med.

.Net har sådan set elimeneret DLL hell for mange år siden. Desværre har det ikke haft noget package manager før indtil for helt nyligt hvor NuGet kom og alle (.Net udviklere) blev glade.

Princippet er det samme som NPM, kopi af alle dller og andre afhængigheder for hvert projekt. Ikke så meget pjat.

  • 0
  • 0
Stephen Aaskov

Den udfordring jeg har, er at få et antal udviklere på et antal projekter til at deles om et antal delte kodekomponenter. Der hjælper OS´ernes pakkehåndtering ikke det store da udviklerne vil præsenteres for et konsistent sourcetræ.

Indtil videre er kandidaterne til en løsning:
- Lav et Git repo pr. projekt, kopier de delte filer ind i det repo efter behov, og lad være med at ændre i dem
- Lav et repo pr. projekt og et eller flere med de delte filer, brug Git submodules til at samle med
- Et tool i stil med Android/Google´s repo der ud fra en manifest fil kan clone de Git repositories koden har afhængigheder til samt lave et symlink hieraki der skaber et konsistent sourcetræ klar til make

En komplikation er så, at ikke alle udviklere skal kunne se al kode. Dette er også den egentlige driver til at opdele koden.

  • 0
  • 0
Benny Simonsen

Jeg har brugt submoduler en del, og ja der er fordele og ulemper afhængig af projektets fase.

Submoduler giver en meget fin sporbarhed for et projekt.
Anken kommer i udviklingsforløbet hvor der er mange ændringer i submodulerne, idet et top-level modul ikke pejer på en branch af et submodul men en given commit - det giver lidt flere commits på top-level modulet, hvis man VIL have refernce til den rigtige version. Jeg kan ikke lige komme i tanke om andre ulemper.

Man kan fint arbejde på f. eks. master i top-level projektet og branch develABC i et submodul. Det er først når man skal dele med andre (incl. Jenkins) man behøver at committe submodulet i top-level projeket.

Når man kloner et repo med submoduler får man komplette repositories som submoduler, det eneste specielle ved submodulet er at det er en given commit der er checket ud og ikke master.

Vil man hellere have samme branch i submoduler som i top-level modulet er det let at lave så man checker master eller samme branch ud som i top-level modulet.

Man kan fint arbejde i et submodul incl. lave branches, push, pull, tag, osv., jeg ved ikke af noget man ikke kan.
Nogle GUI's har dog lidt problemer, f. eks Eclipse(i hvert fald på Windows).

I top-level projektet er der fin synlighed af om der er sket ændringer, såvel committede og ucommittede (dirty) i et submodul. Man stager og committer et submodul på samme måde som en alm. fil.

Top-Level modulet kender ikke til submodulers branches, det er udelukkende hvilken commit der er checket ud top-level modulet kan se. Derfor er top-level modulet stadig up to date unset hvilken branch af submodulet der er checket ud - så længe det er den rigtige commit.

Apropos Jenkins: Heldigvis kan Jenkins kun se top-level modulet. Det er ikke givet at jeg vil have en ny commit i et submodul ønskes testet førend der er sket en ændring i top-level.

Der kan godt være flere niveauer af submoduler, meen der er lidt mere man skal gøre manuelt for at opdatere et sub-submodul.

  • 0
  • 0
Lars Bendix

Efter princippet bedre sent end aldrig ;-)

For mig ser det ud til at det Peter (og visse andre) har brug for, er et versioneringssystem som understøtter omposition model fra Peter Feilers fire arbejdsmodeller. Der definerer man hvordan systemet ser ud med moduler og sub-moduler (eller hvad man nu vil kalde det) og kan meget detaljeret beskrive på hvilken måde man vil udvælge versioner når man kører update/pull eller commit/push.

Og hvis man ikke har et versioneringssystem som direkte understøtter det, så må man selv mere eller mindre mauelt "fake" det. Sådan er det virkelige liv nogle gange så grusomt ;-)

  • 0
  • 0
Jacob Larsen

Jeg går ud fra at det også var det som Google havde brug for til Android. Det er i hvert fald en af hovedfeatures man får med ved at bruge git-repo. Der kan du binde dit projekt op på specifikke commits men også tags eller branches.

  • 0
  • 0
Log ind eller Opret konto for at kommentere