-
1. Začetek
- 1.1 O nadzoru različic
- 1.2 Kratka zgodovina Gita
- 1.3 Kaj je Git?
- 1.4 Ukazna vrstica
- 1.5 Namestitev Gita
- 1.6 Prva nastavitev Gita
- 1.7 Pridobivanje pomoči
- 1.8 Povzetek
-
2. Osnove Git
- 2.1 Pridobivanje repozitorija Git
- 2.2 Snemanje sprememb v repozitorij
- 2.3 Pregled zgodovine potrditev
- 2.4 Razveljavljanje stvari
- 2.5 Delo z daljavami
- 2.6 Označevanje
- 2.7 Aliasi Git
- 2.8 Povzetek
-
3. Veje Git
- 3.1 Veje na kratko
- 3.2 Osnove vej in združevanja
- 3.3 Upravljanje vej
- 3.4 Poteki dela z vejami
- 3.5 Oddaljene veje
- 3.6 Ponovno baziranje
- 3.7 Povzetek
-
4. Git na strežniku
- 4.1 Protokoli
- 4.2 Pridobitev Gita na strežniku
- 4.3 Generiranje vaših javnih ključev SSH
- 4.4 Nastavitev strežnika
- 4.5 Prikriti proces Git
- 4.6 Pametni HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Možnosti gostovanja pri tretjih ponudnikih
- 4.10 Povzetek
-
5. Porazdeljeni Git
- 5.1 Porazdeljeni poteki dela
- 5.2 Prispevek k projektu
- 5.3 Vzdrževanje projekta
- 5.4 Povzetek
-
6. GitHub
-
7. Orodja Git
- 7.1 Izbira revizije
- 7.2 Interaktivno pripravljanje
- 7.3 Shranjevanje na varno (angl. stashing) in čiščenje
- 7.4 Podpisovanje vašega dela
- 7.5 Iskanje
- 7.6 Prepisovanje zgodovine
- 7.7 Demistifikacija ponastavitve
- 7.8 Napredno združevanje
- 7.9 Rerere
- 7.10 Razhroščevanje z Gitom
- 7.11 Podmoduli
- 7.12 Povezovanje v pakete
- 7.13 Zamenjava
- 7.14 Shramba poverilnic
- 7.15 Povzetek
-
8. Prilagoditev Gita
- 8.1 Konfiguracija Git
- 8.2 Atributi Git
- 8.3 Kljuke Git
- 8.4 Primer pravilnika, ki ga uveljavlja Git
- 8.5 Povzetek
-
9. Git in ostali sistemi
- 9.1 Git kot odjemalec
- 9.2 Migracija na Git
- 9.3 Povzetek
-
10. Notranjost Gita
- 10.1 Napeljava in keramika
- 10.2 Objekti Git
- 10.3 Reference Git
- 10.4 Packfiles (datoteke zmanjšanih podatkov)
- 10.5 Refspec
- 10.6 Protokoli prenosa
- 10.7 Vzdrževanje in obnovitev podatkov
- 10.8 Spremenljivke okolja
- 10.9 Povzetek
-
A1. Dodatek A: Git v drugih okoljih
- A1.1 Grafični vmesniki
- A1.2 Git v Visual Studio
- A1.3 Git v Visual Studio Code
- A1.4 Git v IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git v Sublime Text
- A1.6 Git v Bashu
- A1.7 Git v Zsh
- A1.8 Git v Powershellu
- A1.9 Povzetek
-
A2. Dodatek B: Vdelava Gita v vašo aplikacijo
- A2.1 Git v ukazni vrstici
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Dodatek C: Ukazi Git
- A3.1 Nastavitev in konfiguracija
- A3.2 Pridobivanje in ustvarjanje projektov
- A3.3 Osnove posnetkov
- A3.4 Veje in združevanje
- A3.5 Deljenje in posodabljanje projektov
- A3.6 Pregled in primerjava
- A3.7 Razhroščevanje
- A3.8 Popravljanje
- A3.9 E-pošta
- A3.10 Zunanji sistemi
- A3.11 Administracija
- A3.12 Orodja za sisteme napeljave
7.11 Orodja Git - Podmoduli
Podmoduli
Pogostokrat se zgodi, da med delom na enem projektu potrebujete uporabo drugega projekta znotraj tega. Morda gre za knjižnico, ki jo je razvil tretji ponudnik, ali pa jo razvijate ločeno in jo uporabljate v več nadrejenih projektih. V teh primerih se pogosto pojavijo težave: želite, da se lahko dva projekta obravnavata ločeno, hkrati pa lahko uporabite enega znotraj drugega.
Tukaj je primer. Predpostavimo, da razvijate spletno stran in ustvarjate vire Atom. Namesto da bi pisali svojo lastno kodo za generiranje Atomov, se odločite za uporabo knjižnice. Verjetno boste morali vključiti to kodo iz skupne knjižnice, kot je namestitev CPAN ali Ruby gem, ali pa kopirati izvorno kodo v svoje projektno drevo. Težava pri vključevanju knjižnice je, da jo je težko prilagoditi na kakršen koli način in pogosto jo je težje implementirati, saj morate poskrbeti, da je knjižnica na voljo vsakemu odjemalcu. Težava pri kopiranju kode v lasten projekt pa je, da so kakršne koli prilagojene spremembe, ki jih naredite, težke za združevanje, ko postanejo na voljo spremembe izvornega projekta.
Git naslavlja to težavo s pomočjo podmodulov. Podmoduli vam omogočajo, da ohranite repozitorij Git kot podmapo drugega repozitorija Git. To vam omogoča, da klonirate drug repozitorij v svoj projekt in ohranite svoje potrditve ločene.
Začetek s podmoduli
Pojasnili bomo, kako razviti preprost projekt, ki je razdeljen na glavni projekt in nekaj podprojektov.
Začnimo tako, da dodamo obstoječi repozitorij Git kot podmodul repozitorija, na katerem delamo.
Za dodajanje novega podmodula uporabite ukaz git submodule add
z absolutnim ali relativnim URL-jem projekta, ki ga želite začeti slediti.
V tem primeru bomo dodali knjižnico, imenovano »DbConnector«.
$ git submodule add https://github.com/chaconinc/DbConnector
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Privzeto bodo podmoduli dodani v mapo imenovano enako kot repozitorij, v tem primeru »DbConnector«. Če želite, da gre kam drugam, lahko na koncu ukaza določite drugačno pot.
Če zdaj zaženete ukaz git status
, boste opazili nekaj stvari.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: DbConnector
Najprej bi morali opaziti novo datoteko .gitmodules
.
To je konfiguracijska datoteka, ki hrani preslikavo med URL-jem projekta in lokalno podmapo, v katero ste ga povlekli:
[submodule "DbConnector"]
path = DbConnector
url = https://github.com/chaconinc/DbConnector
Če imate več podmodulov, boste imeli več vnosov v tej datoteki.
Pomembno je opozoriti, da je ta datoteka nadzorovana z različicami skupaj z drugimi datotekami, kot je vaša .gitignore
datoteka.
Ta se potiska in vleče skupaj s preostalim projektom.
To je način, kako drugi ljudje, ki klonirajo ta projekt, vedo, kje dobiti projekte podmodulov.
Opomba
|
Ker je URL v datoteki |
Drugi vnos v izpisu git status
je vnos mape projekta.
Če na tem zaženete git diff
, boste videli nekaj zanimivega:
$ git diff --cached DbConnector
diff --git a/DbConnector b/DbConnector
new file mode 160000
index 0000000..c3f01dc
--- /dev/null
+++ b/DbConnector
@@ -0,0 +1 @@
+Subproject commit c3f01dc8862123d317dd46284b05b6892c7b29bc
Čeprav je DbConnector
podmapa v vašem delovnem imeniku, jo Git vidi kot podmodul in ne spremlja njenih vsebin, ko se ne nahajate v tej mapi.
Namesto tega Git vidi to kot določeno potrditev iz tega repozitorija.
Če želite malo lepši izpis diff, lahko ukazu git diff
predložite možnost --submodule
.
$ git diff --cached --submodule
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..71fc376
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "DbConnector"]
+ path = DbConnector
+ url = https://github.com/chaconinc/DbConnector
Submodule DbConnector 0000000...c3f01dc (new submodule)
Ko potrdite, boste videli nekaj takega:
$ git commit -am 'Add DbConnector module'
[master fb9093c] Add DbConnector module
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 DbConnector
Opazite način 160000
za vnos DbConnector
.
To je poseben način v Gitu, ki v bistvu pomeni, da zapisujete potrditev kot vnos v mapi namesto podmape ali datoteke.
Na koncu potisnite te spremembe:
$ git push origin master
Kloniranje projekta s podmoduli
Tu bomo klonirali projekt, ki vsebuje podmodul. Ko klonirate tak projekt, boste privzeto dobili direktorije, ki vsebujejo podmodule, vendar znotraj njih ni še nobene datoteke:
$ git clone https://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
$ cd MainProject
$ ls -la
total 16
drwxr-xr-x 9 schacon staff 306 Sep 17 15:21 .
drwxr-xr-x 7 schacon staff 238 Sep 17 15:21 ..
drwxr-xr-x 13 schacon staff 442 Sep 17 15:21 .git
-rw-r--r-- 1 schacon staff 92 Sep 17 15:21 .gitmodules
drwxr-xr-x 2 schacon staff 68 Sep 17 15:21 DbConnector
-rw-r--r-- 1 schacon staff 756 Sep 17 15:21 Makefile
drwxr-xr-x 3 schacon staff 102 Sep 17 15:21 includes
drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 scripts
drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 src
$ cd DbConnector/
$ ls
$
Mapa DbConnector
obstaja, vendar je prazna.
Zagnati morate dve ukaza: git submodule init
, da inicializirate lokalno konfiguracijsko datoteko, in git submodule update
, da pridobite vse podatke iz tega projekta in izvlečete ustrezno potrditev v nadrejenem projektu:
$ git submodule init
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) registered for path 'DbConnector'
$ git submodule update
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'
Sedaj je vaša podmapa DbConnector
v natančno enakem stanju kot ob vašem prejšnjem potrjevanju.
Obstaja še drug način, ki pa je nekoliko preprostejši.
Če ukazu git clone
dodate --recurse-submodules
, se bodo vsi podmoduli v repozitoriju samodejno inicializirali in posodobili, vključno z vgrajenimi podmoduli, če ima kateri od podmodulov v repozitoriju svoje podmodule.
$ git clone --recurse-submodules https://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) registered for path 'DbConnector'
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'
Če ste že klonirali projekt in pozabili na --recurse-submodules
, lahko koraka git submodule init
in git submodule update
združite tako, da zaženete git submodule update --init
.
Če želite inicializirati, pridobiti in izvleči tudi vse vgrajene podmodule, lahko uporabite enostaven ukaz git submodule update --init --recursive
.
Delo na projektu s podmoduli
Zdaj imamo kopijo projekta z vgrajenimi podmoduli in sodelovali bomo s svojimi sodelavci na glavnem projektu in podprojektu.
Povlek zgornjih sprememb iz daljave podmodula
Najenostavnejši model uporabe podmodulov v projektu bi bil, če bi enostavno uporabljali podprojekt in občasno želeli dobiti njegove posodobitve, vendar ga dejansko ne bi nikakor spreminjali pri vašem izvleku. Pojdimo tu skozi enostaven primer.
Če želite preveriti novo delo v podmodulu, lahko greste v direktorij in poženete git fetch
in git merge
, da posodobite vejo povratnega toka in posodobite svojo lokalno kodo.
$ git fetch
From https://github.com/chaconinc/DbConnector
c3f01dc..d0354fc master -> origin/master
$ git merge origin/master
Updating c3f01dc..d0354fc
Fast-forward
scripts/connect.sh | 1 +
src/db.c | 1 +
2 files changed, 2 insertions(+)
Če greste sedaj nazaj v svoj glavni projekt in poženete git diff --submodule
, lahko vidite, da je bil podmodul posodobljen in dobite seznam vseh potrditev, ki so mu bile dodane.
Če ne želite vsakič vpisovati --submodule
, ko poženete git diff
, lahko možnost nastavite kot privzeti format z nastavitvijo konfiguracijske vrednosti diff.submodule
na »log«.
$ git config --global diff.submodule log
$ git diff
Submodule DbConnector c3f01dc..d0354fc:
> more efficient db routine
> better connection routine
Če v tem trenutku potrdite, boste podmodul zaklenili, da ima novo kodo, ko bodo drugi posodabljali.
Da to naredite, obstaja enostavnejša pot, če imate raje ročno prenašanje in združevanje v poddirektoriju.
Če poženete git submodule update --remote
, bo šel Git v vaše podmodule in vam jih prenesel ter posodobil.
$ git submodule update --remote DbConnector
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
3f19983..d0354fc master -> origin/master
Submodule path 'DbConnector': checked out 'd0354fc054692d3906c85c3af05ddce39a1c0644'
Ta ukaz bo privzeto predvideval, da želite posodobiti izvlek na privzeto vejo oddaljenega podmodula (tistega, na katerega kaže HEAD
na oddaljenem mestu).
Vendar če želite, lahko to nastavite na nekaj drugega.
Na primer, če želite, da podmodul DbConnector
sledi veji »stable« tega repozitorija, lahko to nastavite v datoteki .gitmodules
(tako, da jo spremljajo tudi drugi), ali pa v lokalni datoteki .git/config
.
Nastavimo jo v datoteki .gitmodules
:
$ git config -f .gitmodules submodule.DbConnector.branch stable
$ git submodule update --remote
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
27cf5d3..c87d55d stable -> origin/stable
Submodule path 'DbConnector': checked out 'c87d55d4c6d4b05ee34fbc8cb6f7bf4585ae6687'
Če izpustite -f .gitmodules
, bo naredil samo spremembo za vas, vendar je morda bolj smotrno, da sledite tej informaciji z repozitorijem, da lahko to naredi tudi vsakdo drug.
Ko v tem trenutku poženemo git status
, nam bo Git pokazal, da imamo v podmodulu »nove potrditve«.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: .gitmodules
modified: DbConnector (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
Če nastavite konfiguracijsko nastavitev status.submodulesummary
, vam bo Git tudi pokazal kratek povzetek sprememb na vaših podmodulih:
$ git config status.submodulesummary 1
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: .gitmodules
modified: DbConnector (new commits)
Submodules changed but not updated:
* DbConnector c3f01dc...c87d55d (4):
> catch non-null terminated lines
Če v tem trenutku poženete git diff
, lahko vidimo, da imamo spremenjeno tako našo datoteko .gitmodules
kot tudi, da je veliko potrditev, ki smo jih povlekli, in so pripravljene za potrditev v našem projektu podmodula.
$ git diff
diff --git a/.gitmodules b/.gitmodules
index 6fc0b3d..fd1cc29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
[submodule "DbConnector"]
path = DbConnector
url = https://github.com/chaconinc/DbConnector
+ branch = stable
Submodule DbConnector c3f01dc..c87d55d:
> catch non-null terminated lines
> more robust error handling
> more efficient db routine
> better connection routine
To je kar odlično, saj lahko dejansko vidimo dnevnik potrditev, ki jih bomo ravno potrdili v našem podmodulu.
Ko je enkrat potrjeno, lahko vidimo te informacije tudi po tem, kot tudi, ko poženete git log -p
.
$ git log -p --submodule
commit 0a24cfc121a8a3c118e0105ae4ae4c00281cf7ae
Author: Scott Chacon <schacon@gmail.com>
Date: Wed Sep 17 16:37:02 2014 +0200
updating DbConnector for bug fixes
diff --git a/.gitmodules b/.gitmodules
index 6fc0b3d..fd1cc29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
[submodule "DbConnector"]
path = DbConnector
url = https://github.com/chaconinc/DbConnector
+ branch = stable
Submodule DbConnector c3f01dc..c87d55d:
> catch non-null terminated lines
> more robust error handling
> more efficient db routine
> better connection routine
Ko poženete git submodule update --remote
, bo Git privzeto poskusil posodobiti vse vaše podmodule.
Če jih imate veliko, boste morda želeli podati tudi ime samo tistega podmodula, ki ga želite posodobiti.
Povlek zgornjih sprememb iz daljave projekta
Postavimo se sedaj v vašega sodelavca, ki ima svoj lokalni klon repozitorija MainProject.
Enostaven pogon git pull
, da dobite svoje nove potrjene spremembe, ni dovolj:
$ git pull
From https://github.com/chaconinc/MainProject
fb9093c..0a24cfc master -> origin/master
Fetching submodule DbConnector
From https://github.com/chaconinc/DbConnector
c3f01dc..c87d55d stable -> origin/stable
Updating fb9093c..0a24cfc
Fast-forward
.gitmodules | 2 +-
DbConnector | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: DbConnector (new commits)
Submodules changed but not updated:
* DbConnector c87d55d...c3f01dc (4):
< catch non-null terminated lines
< more robust error handling
< more efficient db routine
< better connection routine
no changes added to commit (use "git add" and/or "git commit -a")
Privzeto ukaz git pull
rekurzivno pridobi spremembe v podmodulih, kar lahko vidimo v izpisu prvega zgoraj navedenega ukaza.
Vendar ne posodobi podmodulov.
To kaže izpis ukaza git status
, ki prikazuje, da je podmodul »spremenjen« in ima »nove potrditve«.
Poleg tega oklepaji, ki kažejo nove potrditve, kažejo levo (<
), kar kaže, da so te potrditve zabeležene v glavnem projektu, vendar niso prisotne v lokalnem izvleku DbConnector
.
Da dokončate posodobitev, morate zagnati git submodule update
:
$ git submodule update --init --recursive
Submodule path 'vendor/plugins/demo': checked out '48679c6302815f6c76f1fe30625d795d9e55fc56'
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
Bodite pozorni, da ste na varni strani, saj bi morali zagnati ukaz git submodule update
z zastavico --init
, če ste pravkar pridobili spremembe v glavnem projektu, ki so dodale nove podmodule, in z zastavico --recursive
, če imajo podmoduli vdelane podmodule.
Če želite ta postopek avtomatizirati, lahko ukazu git pull
dodate zastavico --recurse-submodules
(od Git 2.14 dalje).
To bo Gitu omogočilo, da takoj po potegu zažene ukaz git submodule update
in podmodule postavi v pravilno stanje.
Če želite vedno uporabljati --recurse-submodules
za git pull
, lahko nastavite konfiguracijsko možnost submodule.recurse
na true
(to deluje za git pull
od verzije Git 2.15 dalje).
Ta možnost bo Gitu omogočila, da uporabi zastavico --recurse-submodules
za vse ukaze, ki to podpirajo (razen clone
).
Obstaja posebna situacija, ki se lahko zgodi med posodabljanjem nadrejenega projekta: lahko se zgodi, da je oddaljeni repozitorij spremenil URL podmodula v datoteki .gitmodules
v eni od potrditev, ki ste jo povlekli.
To se lahko zgodi, na primer, če se projekt podmodula spremeni na drugo gostiteljsko platformo.
V takem primeru lahko git pull --recurse-submodules
ali git submodule update
spodleti, če se nadrejeni projekt sklicuje na potrditev podmodula, ki je ni mogoče najti v oddaljenem podmodulu, ki je lokalno konfiguriran v vašem repozitoriju.
Da bi rešili to situacijo, je potreben ukaz git submodule sync
:
# copy the new URL to your local config
$ git submodule sync --recursive
# update the submodule from the new URL
$ git submodule update --init --recursive
Delo na podmodulu
Verjetno uporabljate podmodule, ker resnično želite delati na kodi v podmodulu istočasno, ko delate na glavni kodi (ali med več podmoduli). Drugače bi verjetno namesto tega uporabljali preprostejši sistem upravljanja odvisnosti (kot sta Maven ali Rubygems).
Pojdimo skozi primer spreminjanja podmodula istočasno z glavnim projektom ter hkratnega potrjevanja in objavljanja teh sprememb.
Do sedaj, ko smo zagnali ukaz git submodule update
, da dobimo spremembe iz podmodulov, Git dobi spremembe in posodobi datoteke v poddirektoriju, vendar pusti podrepozitorij v »stanju ločene glave« (angl. detached HEAD state).
To pomeni, da ni lokalne delovne veje (kot je na primer master
), ki bi sledila spremembam.
Če ni delovne veje, ki spremlja spremembe, to pomeni, da bodo spremembe, ki jih potrdite v podmodulu, verjetno izgubljene, ko naslednjič zaženete git submodule update
.
Za sledenje spremembam v podmodulu morate opraviti nekaj dodatnih korakov.
Da bi lažje začeli urejati podmodul, morate storiti dve stvari.
Morate iti v vsak podmodul in izbrati vejo, na kateri boste delali.
Nato morate Gitu povedati, kaj naj naredi, če ste naredili spremembe, in git submodule update --remote
povleče nove spremembe od povratnega toka.
Možnosti so, da jih lahko združite s svojim lokalnim delom, ali pa lahko poskusite postaviti svoje lokalno delo na vrh novih sprememb.
Najprej pojdimo v svoj imenik podmodula in si izvlecimo vejo.
$ cd DbConnector/
$ git checkout stable
Switched to branch 'stable'
Poskusimo posodobiti naš podmodul z možnostjo »združevanja«.
Da ga ročno določimo, lahko enostavno dodamo možnost --merge
našemu klicu update
.
Tu bomo videli, da je bila za ta podmodul sprememba na strežniku in da je združena.
$ cd ..
$ git submodule update --remote --merge
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
c87d55d..92c7337 stable -> origin/stable
Updating c87d55d..92c7337
Fast-forward
src/main.c | 1 +
1 file changed, 1 insertion(+)
Submodule path 'DbConnector': merged in '92c7337b30ef9e0893e758dac2459d07362ab5ea'
Če gremo v direktorij DbConnector
, imamo nove spremembe že združene v našo lokalno vejo stable
.
Sedaj poglejmo, kaj se zgodi, ko naredimo na knjižnici svoje lastne lokalne spremembe in nekdo drug istočasno potisne drugo spremembo na povratni tok.
$ cd DbConnector/
$ vim src/db.c
$ git commit -am 'Unicode support'
[stable f906e16] Unicode support
1 file changed, 1 insertion(+)
Če sedaj posodobimo naše podmodule, lahko vidimo, kaj se zgodi, če imamo tako lokalno kot tudi zgornjo spremembo, ki jo moramo vdelati.
$ cd ..
$ git submodule update --remote --rebase
First, rewinding head to replay your work on top of it...
Applying: Unicode support
Submodule path 'DbConnector': rebased into '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'
Če pozabite --rebase
ali --merge
, bo Git posodobil samo podmodul na karkoli je na strežniku in ponastavil vaš projekt na »stanje ločene glave« (angl. detached HEAD state).
$ git submodule update --remote
Submodule path 'DbConnector': checked out '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'
Če se to zgodi, ne skrbite, enostavno lahko greste nazaj v direktorij in ponovno izvlečete svojo vejo (ki bo še vedno vsebovala vaše delo) ter ročno združite, ali ponovno bazirate glede na origin/stable
(ali katero koli oddaljeno vejo želite).
Če niste potrdili vaših sprememb v vašem podmodulu in poženete submodule update
, ki bi povzročil težave, bo Git prenesel spremembe, vendar ne bo prepisal neshranjenega dela v vašem direktoriju podmodula.
$ git submodule update --remote
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 4 (delta 0)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
5d60ef9..c75e92a stable -> origin/stable
error: Your local changes to the following files would be overwritten by checkout:
scripts/setup.sh
Please, commit your changes or stash them before you can switch branches.
Aborting
Unable to checkout 'c75e92a2b3855c9e5b66f915308390d9db204aca' in submodule path 'DbConnector'
Če ste naredili spremembe, ki so v konfliktu z nečim iz povratnega toka, vas bo Git obvestil, da poženete posodobitev.
$ git submodule update --remote --merge
Auto-merging scripts/setup.sh
CONFLICT (content): Merge conflict in scripts/setup.sh
Recorded preimage for 'scripts/setup.sh'
Automatic merge failed; fix conflicts and then commit the result.
Unable to merge 'c75e92a2b3855c9e5b66f915308390d9db204aca' in submodule path 'DbConnector'
Lahko greste v direktorij podmodula in popravite konflikt, kot bi to običajno storili.
Objavljanje sprememb podmodulov
Sedaj imamo nekaj sprememb v našem direktoriju podmodula. Nekatere od teh so bile prinesene iz povratnega toka z našimi posodobitvami, ostale pa so bile narejene lokalno in niso še nikomur na voljo, saj jih še nismo potisnili.
$ git diff
Submodule DbConnector c87d55d..82d2ad3:
> Merge from origin/stable
> Update setup script
> Unicode support
> Remove unnecessary method
> Add new option for conn pooling
Če naredimo potrditev v glavnem projektu in jo potisnemo navzgor, ne da bi hkrati potisnili tudi spremembe podmodulov, bodo imeli drugi ljudje, ki poskušajo prenesti naše spremembe, težave, saj ne bodo imeli načina, da dobijo spremembe podmodulov, od katerih so odvisni. Te spremembe bodo obstajale samo v naši lokalni kopiji.
Da se to ne zgodi, lahko Git zaprosite, da preveri, ali so bili vsi podmoduli pravilno potisnjeni, preden potisnete glavni projekt.
Ukaz git push
sprejme argument --recurse-submodules
, ki ga lahko nastavimo na »check« ali »on-demand«.
Možnost »check« bo naredila, da bo ukaz push
preprosto spodletel, če niso bile potisnjene vse potrjene spremembe podmodulov.
$ git push --recurse-submodules=check
The following submodule paths contain changes that can
not be found on any remote:
DbConnector
Please try
git push --recurse-submodules=on-demand
or cd to the path and use
git push
to push them to a remote.
Kot vidite, nam prav tako ponuja nekaj koristnih nasvetov o tem, kaj bi morda želeli storiti naslednje.
Preprosta možnost je, da vstopite v vsak podmodul in ročno potisnete na oddaljene strežnike, da se prepričate, da so na voljo od zunaj, nato pa poskusite ta potisk znova.
Če želite, da se ta »check« zgodi za vse potiske, lahko to obnašanje nastavite kot privzeto z ukazom git config push.recurseSubmodules check
.
Druga možnost je uporaba vrednosti »on-demand«, ki bo to poskušala storiti namesto vas.
$ git push --recurse-submodules=on-demand
Pushing submodule 'DbConnector'
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done.
Total 9 (delta 3), reused 0 (delta 0)
To https://github.com/chaconinc/DbConnector
c75e92a..82d2ad3 stable -> stable
Counting objects: 2, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 266 bytes | 0 bytes/s, done.
Total 2 (delta 1), reused 0 (delta 0)
To https://github.com/chaconinc/MainProject
3d6d338..9a377d1 master -> master
Kot vidite, je šel Git v modul DbConnector
in ga potisnil, preden je potisnil glavni projekt.
Če ta poskus potiska podmodula zaradi nekega razloga spodleti, bo spodletelo tudi potiskanje glavnega projekta.
To obnašanje lahko nastavite kot privzeto z ukazom git config push.recurseSubmodules on-demand
.
Združevanje sprememb podmodulov
Če spremenite referenco podmodula istočasno kot nekdo drug, lahko naletite na nekaj težav. To je, če so se zgodovine podmodulov razšle in so bile potrjene v različnih vejah v nadrejenem projektu, bo morda potrebno nekaj dela, da jih popravite.
Če je ena od oddaj neposreden prednik druge (združitev fast-forward), bo Git preprosto izbral slednjo za združitev, zato to deluje brez težav.
Git vam vendarle ne bo poskušal narediti niti trivialnega združevanja. Če se potrditve podmodulov razlikujejo in jih je treba združiti, boste dobili nekaj takega:
$ git pull
remote: Counting objects: 2, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 2 (delta 1), reused 2 (delta 1)
Unpacking objects: 100% (2/2), done.
From https://github.com/chaconinc/MainProject
9a377d1..eb974f8 master -> origin/master
Fetching submodule DbConnector
warning: Failed to merge submodule DbConnector (merge following commits not found)
Auto-merging DbConnector
CONFLICT (submodule): Merge conflict in DbConnector
Automatic merge failed; fix conflicts and then commit the result.
Torej, kar se je tu zgodilo, je, da je Git ugotovil, da se točke v zgodovini podmodula v dveh vejah razhajajo in da jih je treba združiti. Pojasni, kot »združitve naslednjih potrditev niso bile najdene«, kar je zmedeno, vendar bomo to pojasnili malo kasneje.
Za rešitev problema morate ugotoviti, v katerem stanju naj bo podmodul.
Presenetljivo Git ne ponuja veliko informacij, ki bi pomagale pri tem, niti SHA-1 potrditev obeh strani zgodovine.
Na srečo je to preprosto ugotoviti.
Če zaženete git diff
, lahko dobite SHA-1 potrditve, posnete v obeh vejah, ki jih poskušate združiti.
$ git diff
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector
Torej v tem primeru je potrditev eb41d76
v našem podmodulu in c771610
je potrditev, ki jo ima zgornji nivo.
Če gremo v podmapo podmodula, bi ta že morala biti na eb41d76
, saj združevanje ni vplivalo nanj.
Če ni, lahko preprosto ustvarite in preklopite na vejo, da kaže nanj.
Pomembno je, da uporabimo SHA-1 potrditve na drugi strani. To je potrditev, ki jo bomo morali združiti in rešiti morebitne konflikte. Lahko poskusite neposredno združiti s SHA-1, ali pa ustvarite novo vejo in poskusite združiti z njo. Priporočljivo je slednje, tudi če samo zaradi boljše opisne sporočilnosti potrditve združitve.
Zato bomo šli v podmapo podmodula, ustvarili vejo z imenom »try-merge« na podlagi drugega SHA-1 zapisa iz git diff
in jo ročno združili.
$ cd DbConnector
$ git rev-parse HEAD
eb41d764bccf88be77aced643c13a7fa86714135
$ git branch try-merge c771610
$ git merge try-merge
Auto-merging src/main.c
CONFLICT (content): Merge conflict in src/main.c
Recorded preimage for 'src/main.c'
Automatic merge failed; fix conflicts and then commit the result.
Tu dobimo dejansko konflikt pri združevanju, torej če ga rešimo in potrdimo, lahko potem enostavno posodobimo glavni projekt z rezultatom.
$ vim src/main.c (1)
$ git add src/main.c
$ git commit -am 'merged our changes'
Recorded resolution for 'src/main.c'.
[master 9fd905e] merged our changes
$ cd .. (2)
$ git diff (3)
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector
@@@ -1,1 -1,1 +1,1 @@@
- Subproject commit eb41d764bccf88be77aced643c13a7fa86714135
-Subproject commit c77161012afbbe1f58b5053316ead08f4b7e6d1d
++Subproject commit 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a
$ git add DbConnector (4)
$ git commit -m "Merge Tom's Changes" (5)
[master 10d2c60] Merge Tom's Changes
-
Najprej rešimo konflikt.
-
Nato se vrnemo v glavno mapo projekta.
-
Ponovno lahko preverimo vrednosti SHA-1.
-
Rešimo konflikt v vnosu podmodula.
-
Izvedemo potrditev našega združevanja.
Morda je malo zmedeno, vendar ni zelo težko.
Zanimivo je, da Git obravnava še en primer. Če v podmapi obstaja potrditev združitve, ki vsebuje obe zgodovinski potrditvi, jih Git predlaga kot možno rešitev. Vidi, da je nekdo v podmapi projektov združil veji s tema dvema potrditvama, zato je morda to tisto, kar želite.
Zato je sporočilo o napaki od prej »združitve naslednjih potrditev niso bile najdene«, ker tega ni mogel storiti. Zmeda, saj kdo bi pričakoval, da bo poskusil narediti to?
Če najde eno samo potrditev združitve, ki je sprejemljiva, boste videli nekaj takega:
$ git merge origin/master
warning: Failed to merge submodule DbConnector (not fast-forward)
Found a possible merge resolution for the submodule:
9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a: > merged our changes
If this is correct simply add it to the index for example
by using:
git update-index --cacheinfo 160000 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a "DbConnector"
which will accept this suggestion.
Auto-merging DbConnector
CONFLICT (submodule): Merge conflict in DbConnector
Automatic merge failed; fix conflicts and then commit the result.
Predlagan ukaz, ki ga Git ponuja, bo posodobil indeks tako, kot bi to storili z ukazom git add
(ki odpravlja konflikt), nato bo naredil potrditev.
Verjetno tega ne bi smeli storiti.
Prav tako lahko enostavno vstopite v imenik podmodula, preverite, kakšna je razlika, hitro previjete naprej (fast-forward) na to potrditev, jo ustrezno preizkusite in nato potrdite.
$ cd DbConnector/
$ git merge 9fd905e
Updating eb41d76..9fd905e
Fast-forward
$ cd ..
$ git add DbConnector
$ git commit -am 'Fast forward to a common submodule child'
To doseže enako stvar, vendar tako vsaj lahko preverite, da deluje in da imate kodo v vašem direktoriju podmodula, ko končate.
Nasveti za podmodule
Obstaja nekaj stvari, ki jih lahko naredite za malo enostavnejše delo s podmoduli.
Foreach za podmodule
Obstaja ukaz podmodulov foreach
, ki izvede neki poljuben ukaz v vsakem podmodulu.
To je lahko koristno, če imate veliko podmodulov v istem projektu.
Na primer, recimo, da želimo začeti novo lastnost ali popraviti hrošča, in imamo delo v teku na nekaj podmodulih. Enostavno lahko damo vso delo v vseh naših podmodulih v shrambo.
$ git submodule foreach 'git stash'
Entering 'CryptoLibrary'
No local changes to save
Entering 'DbConnector'
Saved working directory and index state WIP on stable: 82d2ad3 Merge from origin/stable
HEAD is now at 82d2ad3 Merge from origin/stable
Nato lahko ustvarimo novo vejo in preklopimo nanjo v vseh naših podmodulih.
$ git submodule foreach 'git checkout -b featureA'
Entering 'CryptoLibrary'
Switched to a new branch 'featureA'
Entering 'DbConnector'
Switched to a new branch 'featureA'
To je razumljivo. Ena izmed resnično uporabnih stvari, ki jo lahko naredite, je, da izdelate dobro poenoteno razliko tega, kar se je spremenilo v vašem glavnem projektu in tudi v vseh vaših podprojektih.
$ git diff; git submodule foreach 'git diff'
Submodule DbConnector contains modified content
diff --git a/src/main.c b/src/main.c
index 210f1ae..1f0acdc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -245,6 +245,8 @@ static int handle_alias(int *argcp, const char ***argv)
commit_pager_choice();
+ url = url_decode(url_orig);
+
/* build alias_argv */
alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
alias_argv[0] = alias_string + 1;
Entering 'DbConnector'
diff --git a/src/db.c b/src/db.c
index 1aaefb6..5297645 100644
--- a/src/db.c
+++ b/src/db.c
@@ -93,6 +93,11 @@ char *url_decode_mem(const char *url, int len)
return url_decode_internal(&url, len, NULL, &out, 0);
}
+char *url_decode(const char *url)
+{
+ return url_decode_mem(url, strlen(url));
+}
+
char *url_decode_parameter_name(const char **query)
{
struct strbuf out = STRBUF_INIT;
Tu lahko vidimo, da smo definirali funkcijo v podmodulu in jo kličemo v glavnem projektu. To je očitno poenostavljen primer, vendar upamo, da vam da idejo, kako je lahko to koristno.
Uporabni aliasi
Morda želite nastaviti nekatere bližnjice za nekatere od teh ukazov, saj so lahko precej dolgi in večine od njih ne morete nastaviti kot privzete možnosti konfiguracije. Nastavljanje bližnjic za Git smo že obravnavali v Aliasi Git, vendar vam tukaj predstavljamo primer, kaj lahko nastavite, če nameravate veliko delati s podmoduli v Gitu.
$ git config alias.sdiff '!'"git diff && git submodule foreach 'git diff'"
$ git config alias.spush 'push --recurse-submodules=on-demand'
$ git config alias.supdate 'submodule update --remote --merge'
Na ta način lahko enostavno poženete git supdate
, ko želite posodobiti vaše podmodule, ali git spush
, da jih potisnete s preverjanjem odvisnosti podmodula.
Težave s podmoduli
Vendar pa uporaba podmodulov ni brez težav.
Preklapljanje vej
Na primer, preklapljanje vej s podmoduli v njimi je lahko malo zapleteno z različicami Gita starejšimi od 2.13. Če ustvarite novo vejo, dodate tja podmodul in nato preklopite nazaj na vejo brez podmodula, boste še vedno imeli podmodul v direktoriju kot nesledeni direktorij:
$ git --version
git version 2.12.2
$ git checkout -b add-crypto
Switched to a new branch 'add-crypto'
$ git submodule add https://github.com/chaconinc/CryptoLibrary
Cloning into 'CryptoLibrary'...
...
$ git commit -am 'Add crypto library'
[add-crypto 4445836] Add crypto library
2 files changed, 4 insertions(+)
create mode 160000 CryptoLibrary
$ git checkout master
warning: unable to rmdir CryptoLibrary: Directory not empty
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
CryptoLibrary/
nothing added to commit but untracked files present (use "git add" to track)
Odstranjevanje direktorija ni težavno, vendar je lahko malo nerazumljivo imeti to tam.
Če ga odstranite in nato preklopite nazaj na vejo, ki ima ta podmodul, boste morali pognati submodule update --init
, da ga ponovno zapolnite.
$ git clean -ffdx
Removing CryptoLibrary/
$ git checkout add-crypto
Switched to branch 'add-crypto'
$ ls CryptoLibrary/
$ git submodule update --init
Submodule path 'CryptoLibrary': checked out 'b8dda6aa182ea4464f3f3264b11e0268545172af'
$ ls CryptoLibrary/
Makefile includes scripts src
Ponovno, ni resnično težavno, vendar je lahko malenkost nerazumljivo.
Novejše različice Git (Git >= 2.13) vse to poenostavijo z dodatkom zastavice --recurse-submodules
pri ukazu git checkout
, kar poskrbi za podajanje podmodulov v pravo stanje za vsako vejo, na katero preklapljate.
$ git --version
git version 2.13.3
$ git checkout -b add-crypto
Switched to a new branch 'add-crypto'
$ git submodule add https://github.com/chaconinc/CryptoLibrary
Cloning into 'CryptoLibrary'...
...
$ git commit -am 'Add crypto library'
[add-crypto 4445836] Add crypto library
2 files changed, 4 insertions(+)
create mode 160000 CryptoLibrary
$ git checkout --recurse-submodules master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
Uporaba zastavice --recurse-submodules
ukaza git checkout
lahko prav tako pride prav, ko delate na več vejah v nadrejenem projektu in ima vsaka vaš podmodul usmerjen na različne potrditve.
Če torej preklopite med vejami, ki beležijo podmodul na različnih potrditvah, se bo ob izvajanju ukaza git status
podmodul pojavil kot »spremenjen« (angl. modified) in bo označeval »nove potrditve« (angl. new commits).
To je zato, ker privzeto stanje podmodula ni preneseno med preklopi vej.
To vas lahko zelo zmede, zato je vedno dobra ideja, da uporabite git checkout --recurse-submodules
, kadar vaš projekt vsebuje podmodule.
Za starejše različice Git, ki nimajo zastavice --recurse-submodules
, lahko po izvleku uporabite git submodule update --init --recursive
, da postavite podmodule v pravilno stanje.
Na srečo lahko Git (>=2.14) vedno uporablja zastavico --recurse-submodules
, če nastavite konfiguracijsko možnost submodule.recurse
: git config submodule.recurse true
.
Kot je omenjeno zgoraj, bo to povzročilo, da se bo Git ponovno pogreznil v podmodule za vsak ukaz, ki ima možnost --recurse-submodules
(razen git clone
).
Prehod iz podimenikov v podmodule
Druga velika težava, s katero se srečuje veliko ljudi, se nanaša na prehajanje iz podimenikov v podmodule.
Če ste spremljali datoteke v svojem projektu in jih želite premakniti v podmodul, morate biti previdni, saj vam Git ne bo naklonjen.
Predpostavimo, da imate datoteke v podmapi svojega projekta in jih želite preklopiti v podmodul.
Če izbrišete podmapo in nato zaženete submodule add
, vam Git javi napako:
$ rm -Rf CryptoLibrary/
$ git submodule add https://github.com/chaconinc/CryptoLibrary
'CryptoLibrary' already exists in the index
Direktorij CryptoLibrary
morate najprej odstraniti iz področja priprave.
Nato lahko dodate podmodul:
$ git rm -r CryptoLibrary
$ git submodule add https://github.com/chaconinc/CryptoLibrary
Cloning into 'CryptoLibrary'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Recimo, da ste to storili v veji. Če poskušate preklopiti nazaj v vejo, kjer so te datoteke še vedno v dejanskem drevesu namesto v podmodulu, boste prejeli to napako:
$ git checkout master
error: The following untracked working tree files would be overwritten by checkout:
CryptoLibrary/Makefile
CryptoLibrary/includes/crypto.h
...
Please move or remove them before you can switch branches.
Aborting
Spremembo lahko prisilite s checkout -f
, vendar bodite previdni, da v tej mapi nimate neshranjenih sprememb, saj bi jih lahko ta ukaz prepisal.
$ git checkout -f master
warning: unable to rmdir CryptoLibrary: Directory not empty
Switched to branch 'master'
Nato, ko se vrnete nazaj, boste iz neznanega razloga dobili prazno mapo CryptoLibrary
in tudi git submodule update
morda ne bo pomagal.
Morda boste morali iti v podmapo podmodula in zagnati git checkout .
, da dobite vse datoteke nazaj.
To lahko za več podmodulov zaženete v skriptu submodule foreach
.
Pomembno je opozoriti, da podmoduli dandanes ohranjajo vse svoje podatke Git v direktoriju .git
glavnega projekta, tako da v primerjavi z veliko starejšimi različicami Git izbris podmodula ne bo izgubil nobenih oddanih potrditev ali vej.
Z uporabo teh orodij lahko podmoduli predstavljajo precej preprost in učinkovit način za razvoj več povezanih, a še vedno ločenih projektov hkrati.