Difference between revisions of "Git basics"
(→Pull) |
(→Pull és Push működése) |
||
Line 448: | Line 448: | ||
==Pull és Push működése== | ==Pull és Push működése== | ||
+ | https://support.gitkraken.com/working-with-repositories/pushing-and-pulling/<br> | ||
A merge és rebase stratégiák nem csak két branch egyesítése közben értelmezett, akkor is mikor egy meglévő branch-en kiadjuk a pull ill. a push parancsot. Ne feledjük el, hogy a git lokálisan is fenntart egy repository-t, amibe commit-al tudunk változásokat bejuttatni. Tehát az SVN-el ellentétben a változásokat a lokális repository-ba kell beadni, majd a PUSH-al ill PULL-al szinkronizáljuk a lokális és távoli repo-t. <br> | A merge és rebase stratégiák nem csak két branch egyesítése közben értelmezett, akkor is mikor egy meglévő branch-en kiadjuk a pull ill. a push parancsot. Ne feledjük el, hogy a git lokálisan is fenntart egy repository-t, amibe commit-al tudunk változásokat bejuttatni. Tehát az SVN-el ellentétben a változásokat a lokális repository-ba kell beadni, majd a PUSH-al ill PULL-al szinkronizáljuk a lokális és távoli repo-t. <br> | ||
A lokális repo-nk egy másolata a távoli repo-nak, minden branch-re (mert hogy az összes branch-et tartalmazza) azt az utolsó commi-t ot tartalmazza, ami a PULL pillanatában a legújabb volt. Aztán ahogy telik az idő, mind a távoli, mind a lokális repositor-nkba ugyan azon a branch-en keletkezhetnek új commit-ok. | A lokális repo-nk egy másolata a távoli repo-nak, minden branch-re (mert hogy az összes branch-et tartalmazza) azt az utolsó commi-t ot tartalmazza, ami a PULL pillanatában a legújabb volt. Aztán ahogy telik az idő, mind a távoli, mind a lokális repositor-nkba ugyan azon a branch-en keletkezhetnek új commit-ok. |
Revision as of 23:08, 12 November 2019
Contents
Áttekintés
A git egyrészről szeretne teljesen elosztott lenne, de valójában ugyan úgy van egy "mester kópia" a szerveren ahogy az SVN-ben, amit mindenki lemásol magához és oda tölti vissza a változásokat.
Főbb tulajdonságok
- Minden felhasználónál a teljes repository megtalálható. Ez azt jelenti, hogy mikor készítünk magunknál egy "másolatot" (szándékosan használtam itt a másolat szót), akkor valójában az egész repository-t lemásoljuk magunkhoz az összes branch-el, tag-el, változtatással együtt. Tehát anélkül hogy hozzá kéne férni a szerverhez, a lemásolás időpontjáig az összes létező lekérdezés lefuttatható mert minden adatunk megvan hozzá. Nyilván ha valaki már felvitte a változtatásit a szerverre, akkor előbb nekünk frissíteni kell a helyi adatbázisunkat, tehát ez szintén csak egy látszat előny, egy átlagos méretű projektben ennek szerintem semmi haszna. Másrészről ha nagyon nagy a projekt, akkor a lokális tárhely szükséglet is gondokat okozhat. Azt mondják, hogy ez az (ál)decentralizáltság azért is jó, mert több helyen megvan pont ugyan az. Ez azért kicsit ferdítés, mert egy valódi szerveren az adatbiztonság mindig meg van oldva, az adatvesztés nem fordulhat elő. Nem is beszélve arról, hogy akárcsak az SVN-nél, a lokális kópiák általában nem a legfrissebbek, a teljes kép mindig csak a szerver kópián van meg.
- Az egyes felhasználóknál lévő lokális kópiák teljes értékű verzió követő rendszerek. Ugyan úgy "commit"-álni kell a lokális kópiánkba, mint az SVN-nél a távoli szerverre. Ez pl nagyon hasznos lehet valakinek, aki csak egy lokális verzió követőt akar felrakni magához gyakorlatilag egy parancs kiadásával. Ha akarjuk, innen lehet feltolni egy másik repozitory-ba, tipikusan a szerverre.
- A branchek közötti váltás csak egy mozdulat: Mivel nálunk a teljes kópia (legalább is annak valamilyen időpillanatban létező változata) ezért egy paranccsal át lehet állni egy adott ágra. Nagy különbség még, hogy egyszerre csak egy ág aktív, amin éppen dolgozunk. Az ágak közötti lokális merge nagyon egyszerű. Ide - oda "beleolvaszthatjuk" a forráskódunkat egyik ágból a másikba, majd bármelyik ágat feltolhatjuk a szerver repository-ba. Ez elég hasznos lehet.
- A repository-k hierarchiába szervezhetőek. Valójában erre lett kitalálva a git. Ez lehetővé teszi akár több ezer együtt dolgozását ugyan azon a giga projekten. Szerintem itt jön elő a git igazi előnye, több ezer ember nehezen használna egyszerre SVN alapú projekteket. Egy giga projektben több fő fejlesztői csoportot kell kialakítani a fő feladatok mentén. Majd azokat további kisebb csoportokra lehet osztani. Git-ben csinálunk egy legfelső repot, ez lesz a legfelső szintű "mester" repo, ezt lemásolják második szintű "mester" repók. Ezt használják a fő fejlesztői csoportok. Nevezzük ezt "második szintű másolatoknak." Ezen repoknak csak olvasási joga van a mester példányhoz. A második szintű másolatokat lemásolják a harmadik szinten lévő fejlesztői csoportok. Ezen harmadik szintű csoportok is csak olvasni tudják a második szintet. A harmadik szintű másolatokhoz már közvetlen fejlesztők kapcsolódnak, oda tolják fel a fejlesztésüket. Az első körös tesztelést önállóan végzik a 3. szinten lévő csoportok. Mikor kész vannak elküldik email-ben a változtatást a 2. szinten lévő szülőjüknek. A második szinten lévő felelősök összegyűjtik a 3. szintről érkezett fejlesztéseket, és belerakják helyben a 2. szintű másolatokba. Ezek után 2. szinten is tesztelésre kerül a fejlesztés. Majd a fejlesztések végül elküldésre kerülnek a mester repóba.
Felépítés
A lokális git másolatunk három részre osztható.
- Lokális, verzió kontrollált másolata a fájloknak. Itt az összes ág és verzió megtalálható a lokális másolat utolsó frissítése óta. (A centrális repó-ban közben már lehet hogy tök más van)
- staging are: ez egy átmeneti tároló. Itt kell összegyűjteni azokat a fájlokat, amiket commit-álni szeretnénk a lokális repónkban. (általában felesleges)
- working copy: ez a jelenlegi munkaterület. Itt a lokális másolat valamelyik ága van berakva (brach). + itt vannak a még nem verziózott fájlok. Ez az ami megjelenik a fájlrendszerben, ha megnyitom a git mappáját a projektnek. Azt hiszem a git szimbolikus linkekkel dolgozik. Ezért tudja olyan gyorsan kicserélni a munkaterületet egy másik branch-re. Ha átállunk egy másik ág-ra, akkor a fájlrendszerben már a másik ág fájljai fognak látszani. Így az svn-nél megszokott mappa / branch szemlélet itt nem célravezető.
Ha van egy üres lokális git repository-nk, akkor az új fájlokat elsőként az add paranccsal hozzá kell adni a staging területhez. Innen a commit paranccsal adhatjuk hozzá az éppen használt ág-hoz. Alapesetben ez a master, vagyis a fő ág. (trunk az SVN-ben).
A megfelelő branch (ág) lokális változatát a push paranccsal tolhatjuk fel a "központi" repository-ba (ugyan abba az ágba). (Akár hogy is nézzük, igazából ez nem egy decentralizált verzió követő rendszer). A központi másolatból a pull paranccsal másolhatjuk át a kiválasztott ágat a mi lokális másolatunk megfelelő ugyan azon ágába.
Note
Fontos különbség még az SVN-hez képest, hogy ha valaki kiad egy push parancsot, és feltolja a lokális változtatásait a lokális repóba, akkor a git úgy veszi hogy az egész branch módosult
. Ha valaki egy másik felhasználó push parancsa után egy tök másik fájlt akar feltolni ugyan abba a branch-be, akkor csak akkor fogja megtenni, ha az új változtatásokat elsőként leszedi a pull paranccsal, lokálisan egyesíti a fájlokat, majd utána az egészet feltolja.
Támogatott protokollok
A felhasználók (kliensek) a központi példányt ssh, http vagy git protokollon keresztül érhetik el. A változásokat a push paranccsal tolhatják fel a központi példányba, és a pull paranccsal hozhatják le.
Mi az ssh protokollt fogjuk használni, mivel ha van ssh hozzáférés egy szerverhez, akkor azonnal használható minden további komponens nélkül. Tehát ssh esetén nem kell külön egy git damon fusson a szerveren mint az SVN esetében. A centrális példány is csak fájlok összessége, amihez ssh-val hozzáfér a lokális git kliens.
Note
Azt kell látni, hogy az svn-el ellentétben az esetek többségében nem fut egy git daemon a szerver gépen
A lokális git programunk az adott (fájl megosztást is támogató) protokollon keresztül eléri a szerveren lévő fájlokat. Tehát nincs más dolgunk, mint a klienseknek egy olyan URL-t biztosítani, ami az adott központi git repository mappájára mutat. A többit elintézi a git.
- HTTP: az apache-ban készíteni kell hozzá egy virtuális hosztot, kb negyed óra működésre bírni. Az autentikációt az apache végzi, előnye, hogy anonymous hozzáférést nagyon egyszerű vele biztosítani.
- SSH: ehhez semmit nem kell konfigurálni. Ha egy felhasználó ssh-n keresztül képes belépni, és hozzáférni a git központi repository fájljaihoz, akkor azonnal használni tudja azt, nincs szükség egyéb konfigurációra. Pont ezért ez a legelterjedtebb protokoll.
- GIT: ez a git saját protokollja, kifejezetten anonymous hozzáférésre készült az open source világ számára.
Repository létrehozás
Nem feltétlen kell központi git repository, azt is megtehetjük, hogy csak egy lokális repository-t inicializálni, de akkor nem tudunk együtt dolgozni másokkal. Ezért mi elsőként egy központi repository-t fogunk készíteni a szerveren, majd a clone paranccsal ezt fogjuk lemásolni a munkaállomásokra mint lokális másolatok.
Központi másolat
Álljunk bele abba a mappába ahol a központi másolatot ki szeretnénk alakítani a szerveren, majd a git init paranccsal inicializálhatjuk a centrális repository-t. Itt megint megemlíteném, hogy két speciális kapcsolót kell használni az init parancs mögött, amivel jelezzük, hogy ez lesz a központi (mester) példány.
- --bare: Ennek a hatására a szerveren egy speciális repository fog létrejönni, ahol nem lesz staging vagy working area, a fájlok binárisan lesznek tárolva, ember számára nem lesz olvasható a tartalom. Ez kifejezetetten szerver üzemmódra van, itt nem lehet használni a commit vag az add parancsokat.
- --share: Szintén az jelzi, hogy ez a szerver példány, amihez többen fognak hozzáférni, így a git a push paranccsal megkapott fájlok tulajdonosát speciálisan fogja beállítani, hogy a csoportból mindenki hozzáférjen.
Tehát, inicializáljuk
$ cd /home/adam/git/testRepo $ git init --bare --shared
Ekkor létrejött a szerver git struktúra:
$ ll /home/adam/git/testRepo total 32 drwxrwsr-x. 2 adam adam 4096 Sep 29 16:57 branches -rw-rw-r--. 1 adam adam 126 Sep 29 16:57 config -rw-rw-r--. 1 adam adam 73 Sep 29 16:57 description -rw-rw-r--. 1 adam adam 23 Sep 29 16:57 HEAD drwxrwsr-x. 2 adam adam 4096 Sep 29 16:57 hooks drwxrwsr-x. 2 adam adam 4096 Sep 29 16:57 info drwxrwsr-x. 13 adam adam 4096 Sep 29 17:59 objects drwxrwsr-x. 4 adam adam 4096 Sep 29 16:57 refs
SSH-val az alábbi URL-n tudják a kliensek lemásolni a központi másolatot:
git clone ssh://<user>@<host>/<full path to git repository directory>
A mi esetünkben:
git clone ssh://adam@jenkins.mycompany.hu/home/adam/git/testRepo/
Lokális másolatok
SSH kulcsok beállítása
Ahhoz hogy ne kérjen jelszavat minden egyes git művelet, el kell helyezzük a titkos kulcsunkat a saját számítógépünkön, aminek a publikus párja a git-et futtató ssh szerveren van. Azonban a titkos kulcsunkat az ssh kliens a ~./ssh/id_rsa néven fogja keresni. Ha a git -hez való titkos kulcsot rakjuk ide, akkor nem fogunk tudni más szerverhez kulccsal kapcsolódni. Erre találták ki a ~./ssh/config fájlt, ahol fel lehet sorolni, hogy milyen hosztokhoz milyen user névvel és titkos kulccsal csatlakozzon az ssh kliens.
# touch ~./ssh/config # chmod 600 ~./ssh/config
Majd az alábbi tartalmat helyezzük el benne minden egyes hoszt-hoz:
Host 192.168.10.121 IdentityFile /home/adam/auth/ssh/titkos_kulcs User adam Host hostname2.com IdentityFile /home/adam/auth/ssh/titkos_kulcs2 User adam2
Warning
Fontos hogy a config fájlon és a kulcsokon is 600 jog legyen
Központi példány másolása
Álljunk bele a lokális gépen abba a mappába, ahol a git repository-k vannak, de ne hozzunk létre a most másolandó repónak egy saját mappát, a copy parancs ezt majd megteszi.
Adjuk ki a git clone parancsot. Ha nem kulcsos az azonosítás az ssh szerveren, akkor be kell írjuk a jelszavunkat minden egyes hozzáféréskor.
$ cd /home/adam/git $ git clone ssh://adam@jenkins.mycompany.hu/home/adam/git/testRepo/ Cloning into 'testRepo'... adam@jenkins.mycompany.hu's password: remote: Counting objects: 9, done. remote: Compressing objects: 100% (3/3), done. remote: Total 9 (delta 1), reused 0 (delta 0) Receiving objects: 100% (9/9), done. Resolving deltas: 100% (1/1), done. Checking connectivity... done.
Ezzel a /home/adam/git mappában létrejött a repository nevével megegyező testRepo mappa. Láthatjuk, hogy a szerver struktúrával ellentétben, most létrejött a .git mappa. Ha nem szerver módban hozzuk létre a repository-t, akkor létrejön a working area is, ami egy fajta virtuális fájlrendszer.
Git beállítások
Végezzük el az alap git beállításokat. A git a felhasználó home mappájában a .gitconfig fájlban tárolja a beállításokat. Ezt vagy kézzel írjuk közvetlenül, vagy a git config --global paranccsal.
$ git config --global user.name "Berki Adam" $ git config --global user.email berki.adam@mycompany.hu $ git config --global core.editor mcedit $ git config --global diff.tool meld
Ekkor a ~/.gitconfig fájlban az alábbi sorok jöttek létre:
[user] name = "Berki Ádám" email = berki.adam@mycompany.hu [core] editor = mcedit [diff] tool = meld
Git alap parancsok
Az alábbi képen láthatjuk, hogy mi történhet egy fájllal a lokális másolatunkban:
A munkaterületen egy fájl értelem szerűen, vagy még nincs hozzáadva a repository-hoz, vagy hozzá van már adva és módosítva van, vagy hozzá van már adva és nincs is módosítva.
STAGE
Fájl hozzáadása a stage területhez: git add <filename>
$ git add file1.txt file2.txt file3.txt
Ha vissza akarunk vonni egy fájlt a stage területről, akkor ezt a git reset HEAD <file name> paranccsal tehetjük meg.
COMMIT ügyek
A commit parancs a lokális repository-ba a stage területről rakja be a fájlokat (tehát csak azokat amiket már a stage-hez hozzáadtunk): git commit -m 'commit üzenet'
$ git commit -m 'Első commit' [master 8dd4892] Első commit 4 files changed, 3 deletions(-) delete mode 100644 elsofile.txt create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt
Ha nem akarjuk külön hozzáadni a stage területhez a fájlokat, akkor a -a kapcsolóval mindent be lehet commit-álni, ami változott:
$ git commit -a -m 'comment' [master 80e912f] comment 4 files changed, 3 insertions(+) create mode 100644 file4.txt
Mielőtt a szerverre feltolnánk a repository jelenlegi tartalmát (push) vissza tudjuk vonni a legutolsó commitot: git commit --amend
Ha egy fájlt vissza akarunk állítani az eredeti állapotába: git checkout -- <filename>
status
A munkaterületen a fájlok állapotának kiírása: git status -s
$ git status -s M file1.txt MM file2.txt M file3.txt ?? file4.txt A file5.txt
A git status-nak a kimenete a következőt jelenti. <stage terület státusz><munkaterület státusz> <file néve>
- Tehát az első oszlopban ha 'M' betű van, azt jelenti, hogy módosult, de hozzá van adva a stage-hez.
- Ha a második oszlopban van 'M' betű, akkor azt jelenit, hogy olyan módosítást tartalmaz, ami még a stage-hez nincs hozzáadva.
- Ha mind két oszlopban 'M' betű van, azt jelenit, hogy már hozzá lett adva a stage-hez, de azóta módosult, tehát ha most nyomnánk egy commit-et, akkor csak az kerülne bele, amit a stage-hez adás előtt beleírtunk.
- A '??' (tehát mind két oszlopban) jel azt jelenti, hogy új fájl, ami még nem volt hozzáadva a stage-hez sem.
- Az 'A' az első oszlopban azt jelenit, hogy új fájl, de már hozzá van adva a stage-hez, de még sosem volt commitálva.
Ha a -b kapcsolót is belerakjuk a lekérdezésbe, akkor a brach infót is megmutatja:
$ git status -bs ## master...origin/master [ahead 1] ...
DIFF
Azt hogy a munkaterületen mi változott a diff paranccsal tudjuk megnézni. Ez a stage területen lévő fájlokat nem mutatja. Nem igazán ember barát a kimenet:
$ git diff diff --git a/file1.txt b/file1.txt index e7cb5c3..020c78b 100644 --- a/file1.txt +++ b/file1.txt @@ -1 +1 @@ -mod \ No newline at end of file +moddd \ No newline at end of file
Ha a --staged kapcsolót is mögé írjuk, akkor azt mutatja meg hogy milyen változás van a stage-en. (Gyakorlatilag ez a state tartalma)
Ha könnyen értelmezhető kimenetet szeretnénk látni, akkor használjuk a meld-et. Ezt már korábban beállítottuk a ~/.gitconfig-ban mint alapértelmezett diff eszköz.
$ git difftool -y -d
- -y: ne kérdezzen rá
- -d: mappa szinten hasonlítson össze. Enélkül egyenként feldobálja az összes fájlt külön meld ablakban.
REMOVE
Az rm paranccsal a fájlt a teljes virtuális fájlrendszerből töröljük, tehát nem csak a lokális repository-ból, hanem a stage és a munkaterületről is el fog tűnni:
$ git rm file1.txt error: the following file has local modifications: file1.txt (use --cached to keep the file, or -f to force removal)
Ha csak verziózatlan fájlt akarunk belőle csinálni a munkaterületen, akkor ezt az rm --cached kapcsolóval tehetjük meg. Ettől még a munkaterületen meg fog maradni:
$ git rm --cached file2.txt rm 'file2.txt'
Commit history
A git log parancs megmutatja az eddigi teljes commit listát:
$ git log commit 80e912f1c37ca3237b8248ad7ebb106aca17da59 Author: Berki Ádám <berki.adam@mycompany.hu> Date: Fri Sep 30 13:21:21 2016 +0200 Második commit üzenet commit 8dd4892161ced552752512c0a9b082c5c1dec6f6 Author: Berki Ádám <berki.adam@mycompany.hu> Date: Fri Sep 30 12:14:48 2016 +0200 Első commit üzenet ... ....
Rengetegféle képen lehet szűkíteni a megjelenített listát.
Szerver ügyek
Távoli repók listája
Távoli elérések listázása (központi repository-k):
$ git remote -v origin ssh://adam@jenkins.mycompany.hu/home/adam/git/testRepo/ (fetch) origin ssh://adam@jenkins.mycompany.hu/home/adam/git/testRepo/ (push)
Külön kiírja, hogy melyik repóra milyen jogunk van (olvasás - fetch, írás - push)
Az első oszlopban a kapcsolat neve látható. Ezzel kell rá hivatkozni, ha fel ill. le akarunk tölteni a szerverről. Jelen esetben csak egy távoli szerver van beállítva, aminek a neve origin, ami azt jelöli, hogy ez az a repó, amiből klónoztuk a mi lokális példányunkat.
Egy adott kapcsolatról remote show <név> paranccsal kaphatunk részletesebb infót:
$ git remote show origin adam@jenkins.mycompany.hu's password: * remote origin Fetch URL: ssh://adam@jenkins.mycompany.hu/home/adam/git/testRepo/ Push URL: ssh://adam@jenkins.mycompany.hu/home/adam/git/testRepo/ HEAD branch (remote HEAD is ambiguous, may be one of the following): adam2 master Remote branches: adam1 tracked adam2 tracked master tracked Local branch configured for 'git pull': master merges with remote master Local ref configured for 'git push': master pushes to master (fast-forwardable)
Frissítés a szerveröl
Két lehetőségünk van. A pull parancs minden változást letölt, és merge-l is a lokális repóba. A fetch parancs letölti a változásokat, de nem merege-li automatikusan, ezt majd nekünk kell megtenni.
$ git fetch adam@jenkins.mycompany.hu's password:
ill.
$ git pull adam@jenkins.mycompany.hu's password:
Mind két parancs mögött meg lehet adni a kapcsolat nevét (ha több távoli szerver is be van állítva, nálunk csak egy origin nevű van) és a branch nevét, amit fel akarunk tölteni. Ha nem adjuk meg egyiket sem, akkor nálunk az origin kapcsolatot fogja használni, valamit azt a branch-et, ami éppen a munkaterületre be van töltve. (jelenleg ez a master, ami SVN terminológiában a trunk)
git pull origin <branch neve>
Frissítés a szervere
Branch
Git-ben minden egyes commit egy pillanat felvétele a világnak. Ezen pillanat felvételek egymásra mutatnak. (Baloldalon van a legrégebbi, és ahogy haladunk előre az időben mindig egy újabb commit lesz leglelől, ami rá fog mutatni az előzőre. Egy branch nem más mint egy kitüntetett mutató egy adott pillanat felvételre (narancssáraga). Ha egy branch-be kommitálunk, akkor egy saját commit láncot indítunk el arról pontról. A git úgy tartja nyilván az aktuálisan kiválasztott branch-et (ami be vat töltve a munkaterületre) hogy ráállítja a speciális HEAD mutatót. Igazából branch váltás közben nem csinál mást, mint hogy a HEAD mutatót átállítja egy másik branch-re, és az abban a snapshot-ban lévő fájlokat tölti be a munkaterületre.
A fenti képen az látható, hogy két branch-ünk van. A master branch (amit a git init magától létrehoz, hogy legalább egy branch legyen a repóban) és a test nevű branch. Jelenleg a munkaterületünkre a master van betöltve, mivel arra mutat a HEAD mutató. A test branch-be már volt egy commit a master hez képest.
Tip
A master is közönséges branch amit a git init hoz létre, és ráállítja a HEAD mutatót kapásból
Tip
A tag is egy speciális mutató, ami rámutat egy snaphost-re, de nem lehet mozgatni, vagy betölteni a munkaterületre (lásd lentebb).
Branch kezelése
Létrehozás
Branch-et a branch <ág neve> paranccsal hozhatunk létre:
$ git branch adam3
Váltás
A checkout paranccsal válthatunk branch-et.
Warning
Fontos, hogy a munkaterületen nem lehetnek verziózatlan vagy módosított fájlok, mert a teljes munkaterület ki fog cserélődni. Ezért amíg nincs mind a repóban, addig nem fogja hagyni a branch váltást
$ git checkout adam3 Switched to branch 'adam3'
Tip
A branch váltást tartják a git legerősebb tulajdonságának, tényleg egy pillanat alatt lehet két branch között váltani, mivel a teljes másolattal rendelkezik minden kliens
Listázás
Jelenlegi branch listázásához a git status parancsot használhatjuk:
$ git status On branch adam3 ...
A branch-eket a git branch paranccsal listázhatjuk (paraméter nélkül)
$ git branch adam3 testBranch * master
A csillag azt a bracnh-et jelöli, ahol éppen állunk.
Listázzuk ebből csak azokat, akiket még nem mergel-tünk bele a jelenlegi branch-be (ahol most állunk)
$ git branch --no-merged testBranch
Note
A --no-merged paraméter csak azokat a bracnh-eket fogja listázni, amik nem ugyan arra a commit-ra mutatnak mint a jelenlegi branch
És listázzuk azokat akiket már mergel-tünk a jelenlegibe:
$ git branch --merged adam3 * maste
Note
A --merged azokat is mutatja, akiknek a mutatója pont arra a commit-ra mutat, ahol most állunk
Merge
Mindig úgy kezdjük a merge-t hogy beleállunk abba a branch-be, AHOVA mergelni akarunk, majd kiadjuk a merge <branch neve AHONNAN> parancsot. Két esetet kell megkülönböztetni.
Az alább parancs a master-be merge-li az adam3 branch-et.
$ git checkout master $ git merge adam3
Kétféle merge létezik:
1. Egy szülős (Fast-forward) Ebben az esetben az a branch, ahonnan mergelni akarunk egyenes ági leszármazottja a jelenlegi branch-nek. Az alábbi ábrán pl a hotfix-et ha mergeljük a master -be.
Ebben az esetben a git nem csinál mást, mint előre mozgatja a master mutatóját (fast-forward). Persze ha volt változás, akkor a változásokat elsőként egyesíteni kell és az esetleges konfliktusokat feloldani. Ha fastForward merge történik, a git ezt jelzi:
$ git merge adam3 Updating f42c576..3a0874c Fast-forward ...
2. Két szülős
Ebben az esetben az a branch ahonnan másolni akarunk (iss53), már nem közvetlen leszármazottja annak a branch-nek ahova merge-ni szeretnénk (master).
Ebben az esetben nem lehet egyszerűen előre mozgatni a mutatót. A git meg fogja keresni a közös őst, és egy három utas egyesítést fog csinálni a közös ősből, a jelenlegi branch -ből (a példában a master) és a bemásolandó branch-böl (a példában az iss53), és ebből az egészből létre fog hozni egy új commit-ot.
A mi példánkban a c6 lesz létrehozva a c5 c4 és c2 commit-okból.
Rebase
Pull és Push működése
https://support.gitkraken.com/working-with-repositories/pushing-and-pulling/
A merge és rebase stratégiák nem csak két branch egyesítése közben értelmezett, akkor is mikor egy meglévő branch-en kiadjuk a pull ill. a push parancsot. Ne feledjük el, hogy a git lokálisan is fenntart egy repository-t, amibe commit-al tudunk változásokat bejuttatni. Tehát az SVN-el ellentétben a változásokat a lokális repository-ba kell beadni, majd a PUSH-al ill PULL-al szinkronizáljuk a lokális és távoli repo-t.
A lokális repo-nk egy másolata a távoli repo-nak, minden branch-re (mert hogy az összes branch-et tartalmazza) azt az utolsó commi-t ot tartalmazza, ami a PULL pillanatában a legújabb volt. Aztán ahogy telik az idő, mind a távoli, mind a lokális repositor-nkba ugyan azon a branch-en keletkezhetnek új commit-ok.
A fenti példában a távoli repo-ban lévő master -en van egy olyan commit (C2`) ami az utolsó pull után keletkezett, így a lokális repo-ban nincs meg. A lokális repo-ban van is van két új commit a master-hez képest.
GitKraken-ben ez a következő képen nézne ki:
A lentebbi kék doboz az origin (original repository, vagyis ahonnan a lokálisat klónoztuk) a fentebbi zöld doboz pedig a lokális mutatónk. Láthatjuk, hogy a távoli repo-ban a master branch-en vagy a 'second-commit' ami lokálisan hiányzik, viszont lokálisan van 2 új commit, ami a távolin nincs meg.
Push
Ha a távoli repo-ban akkor tudunk egyszerűen push-olni, ha ott nincs olyan commit ami a lokálisan nincs meg. Nézzük az alábbi példát:
A fenti példában az origin-on (remote) lévő utolsó commit lokálisan is megtalálható. Ezért a push minden további nélkül lehetséges. Ebben az esetben a git egy fast-forward merge-t fog csinálni, vagyis fogja a lokális új commit-okat, ráfűzi a remote branch végére, majd átállítja a master mutatót:
A gond csak akkor van, ha a remote-on (origin) már van új commit. Ebben az esetben a fast-forward merge nem lehetséges, a GitKraken pl az alábbi figyelmeztetést fogja adni:
Egyrészt felajánlja hogy elsőként futtassunk egy PULL-t (amiből jelen esetben 3-way merge lenne, lásd a #Pull fejezetben), vagy a drasztikus Force push-t. A Force-push a teljes távoli repository commit history-t felülírja a lokális commit history-val, úgy hogy minden olyan commit el fog veszni, ami lokálisan nem volt meg. Ez egy visszavonhatatlan lépés. A GitKraken figyelmeztet is rá:
Ha tényleg a Force push-t választjuk, akkor a fenti példában a 2. commit (ami nem volt meg lokálisan) eltűnik, és az új log fa így néz ki:
Pull
Pull esetében 4 lehetőségünk van, ami GitKraken esetében így fest:
- Fetch: nem frissíti a lokális repot, csak letölti a commit "meta" adatokat a váli repo-bol, hogy ki tudja rajzolni a gráfot.
- Fast-forward only: Ez megfelel a fenti leírt Push működésnek, csak fordítva. Vagyis a távoli branch-ben vannak új commit-ok a lokálishoz képest, de lokálisan nincs új commit, nincs olyan commit, ami a remote-ban ne szerepelne. Ebben az esetben a lokálisan hiányzó commit-okat a git a hozzá fogja biggyeszteni a lokális branch végéhez, majd a lokális branch mutatót át fogja állatni. (lokális fast-forward).
- Fast-forward if possible: Ebben az esetben meg fogja próbálni a fast-forward-ot, de ha nem lehetséges, akkor a 3 utas merge-t fogja alkalmazni, és létre fog hozni egy merge commit-ot, aminek a nevében is benne lesz, hogy ez miért keletkezett:
- Rebase: Lokálisan, a távoli utolsó commit-ra rá fogja fűzni a lokális új commit-okat, így nem lesz plusz leágazás a commit-logban, az egész egy folytonos vonal esz, viszont elveszik az az információ, hogy a remote és a local elmászott egymástól (ami egyáltalán nem baj, tisztán tartja a commit history-t). GitKráken-ben így néz ki egy Rebase-elt Pull:
Természetesen nem veszett el (nem úgy mint a Force Puhs-nál), a távoli 'remote commit2' -re ráfűzte a 'local commit' és 'local-commit2' változtatásokat, így már tudnánk push-olni.
Konfliktus feloldása
Meld mint merge tool
Elsőként a ~/.gitconfig fájlba be kell állítsuk, hogy a meld-et használja mint merge eszköz, ezen felül, hogy a meld három ablakát hogyan töltse fel:
... [merge] tool = meld [mergetool "meld"] cmd = meld "$LOCAL" "$MERGED" "$REMOTE" --output "$MERGED"
Konfliktus előidézése
Ha egy adott fájl részletbe mind két branch-ben módosítottak, akkor a git nem fogja tudni magától feloldani a konfliktust. Ezek a fájlok nem kerülnek automation commit-álásra a jelenlegi branch-hez.
Próbáljuk ki. Álljunk bele az adam3-as brach-be. Majd itt módosítsuk a file1.txt-t, majd commit-áljuk be:
$ git checkout adam3 Switched to branch 'adam3' $ echo "branch után írtam bele" >> file1.txt $ git commit -a -m 'comment az adam3-ban' [adam3 7305eb6] comment az adam3-ban 1 file changed, 3 insertions(+), 1 deletion(-)
Most lépjünk át a master branch-be, majd ott is írjunk bele a file1.txt-be:
[adam@adamdell testRepo]$ git checkout master Switched to branch 'master' Your branch is up-to-date with 'origin/master'. $ echo "ezt a tag készítés után írtam bele" >> file1.txt $ git commit -a -m 'comment a master-ban' ...
Most adjuk ki a merge parancsot, vagyis hogy az adam3-as branch-et merge-jük a master-be (ahol most állunk)
$ git merge adam3 Auto-merging file1.txt CONFLICT (content): Merge conflict in file1.txt Automatic merge failed; fix conflicts and then commit the result.
Láthatjuk, hogy a konfliktus van. A git status parancs további infókkal szolgálhat:
$ git status On branch master Your branch is up-to-date with 'origin/master'. You have unmerged paths. (fix conflicts and run "git commit") Unmerged paths: (use "git add <file>..." to mark resolution) both modified: file1.txt no changes added to commit (use "git add" and/or "git commit -a")
Ha belenézünk a munkaterület virtuális fájlszerkezetébe láthatjuk, hogy megjelentek a szabványos konfliktus fájlok:
ll total 28 ... -rw-rw-r-- 1 adam adam 119 Sep 30 15:31 file1.txt.BACKUP.7629.txt -rw------- 1 adam adam 3 Sep 30 15:31 file1.txt.BASE.7629.txt -rw------- 1 adam adam 49 Sep 30 15:31 file1.txt.LOCAL.7629.txt -rw------- 1 adam adam 33 Sep 30 15:31 file1.txt.REMOTE.7629.txt
Konfliktus feloldása
Ki kell adjuk a git mergetool parancsot, ami az összes konfliktusban lévő fájlra meg fogja nyitni a meld-et három ablakos nézetben. Középen lesz a végeredmény, bal oldalon a saját, még jobb oldalon a mergelni kívánt branch-ben lévő változat. (így állítottuk be a .gitconfig-ban.
$ git mergetool Merging: file1.txt Normal merge conflict for 'file1.txt': {local}: modified file {remote}: modified file
Ha becsukjuk a meld-et, akkor rá fog kérdezni, hogy sikeres volt e a merege:
file1.txt seems unchanged. Was the merge successful? [y/n] n merge of file1.txt failed
Ha mentés nélkül csukjuk be a meld-et akkor rákérdez, hogy készen vagyunk e. Ha itt n-t választunk, akkor semmi sem változik. A gt mergetool-al újra meg tudjuk nyitni a meld-et. Ha mentjük a végeredményt (középső csík), akkor kérdés nélkül felszámolja a segéd fájlokat és egy .orig végződésű fájlban elmenti az eredeti fájlt.
Nézzük meg, hogy most a file1.txt-ben a merge közben összeállított tartalom van. Nézzük meg a státuszt:
$ git status -s M file1.txt ?? file1.txt.orig
Nincs más dolgunk, mint a file1.txt-t hozzáadni a stage területhez, majd nyomni egy commit-ot:
$ git add file1.txt $ git commit -m 'mege után' [master bb194c8] mege után
Tag
Git-ben a tag-ek egy adott branch egy adott pontjának a pillanatfelvételei. (Valójában akár csak az SVN-ben, a branch és a tag egy pillanatfelvétele a világnak, de a tag nem változik, a tag mutatója mindig ugyan arra a commit-ra fog mutatni, ezt nem lehet megváltoztatni git-ben.
Tip
A tag is egy ponter, ami egy commit-re, pillanat felvételre mutat rá, csak nem lehet módosítani
Kétféle tag van:
- Lightweight: ez egy ugyan olyan mutató mint a branch, csak fixen egy helybe marad, míg a branch további életet él.
- Annotated: Az egész pillanatfelvételt lemásolja (nem csak mutató), ellenőrző kódot, aláírást készíthetünk hozzá, és rengeteg plusz paramétert elmenthetünk hozzá.
Tag készítése
Pehelysúlyú tag-et a git tag <tag név> paranccsal készíthetünk:
$ git tag v1.1
Ez egy pillanat felvétel a jelenlegi branch legutolsó commit-járólé.
Ha a -a, -s, -m kapcsolók valamelyikével kiegészítjük a parancsot, akkor Annotated tag fog készülni:
$ git tag -a v1.4 -m "ez a 1.4-es verzió commentje"
Tag listázása
Listházáshoz használjuk a git tag parancsot: $ git tag v1.1
Ha már látjuk melyik tag érdekel, akkor a git show <tag név> paranccsal részletes infót kaphatunk. Láthatjuk a tag paramétereit (ha annotált volt) és hogy melyik commit-ból jött létre:
$ git show v1.4 tag v1.4 Tagger: Berki Ádám <berki.adam@mycompany.hu> Date: Fri Sep 30 14:22:21 2016 +0200 ez a 1.4-es verzió commentje commit 80e912f1c37ca3237b8248ad7ebb106aca17da59 Author: Berki Ádám <berki.adam@mycompany.hu> Date: Fri Sep 30 13:21:21 2016 +0200 comment ...
Tag betöltése
A tag-be úgy lehet belenézni, ha csinálunk belőle egy új branch-et. Ehhez a checkout parancsot kell meghívni -b kapcsolóval: checkout -b <új branch name> <stource tag név>
$ git checkout -b branchFromV1.4 v1.4 Switched to a new branch 'branchFromV1.4'
Tag-ek feltöltése
A tag-et a git push parancs nem tölti fől. Ezt implicit meg kell adni: git push <kapcsolat név> <tag neve, amit fel akarunk tölteni>
$ git checkout -b branchFromV1.4 v1.4 Switched to a new branch 'branchFromV1.4' [adam@adamdell testRepo]$ git push origin v1.4 adam@jenkins.mycompany.hu's password: Counting objects: 9, done. Delta compression using up to 4 threads. Compressing objects: 100% (5/5), done. Writing objects: 100% (9/9), 735 bytes | 0 bytes/s, done. Total 9 (delta 0), reused 0 (delta 0) To ssh://adam@jenkins.mycompany.hu/home/adam/git/testRepo/ * [new tag] v1.4 -> v1.4
Rapid SVN
Új BitBucket repo klónozása
A SmartGit sok féle GIT szolgáltató integrációját tartalmazza. Lehetőség van rá, hogy egy Git szolgáltatót mint pl a BitBucket felvegyünk mint Host Provider, ami később lehető teszi, hogy klónozásnál egyszerűen válogathatunk az elérhető repók listájából anélkül, hogy tudnánk mi az adott repo clone URL-je. Fel fogunk venni egy BitBucket szolgáltatót.
Állítsuk be, hogy ne legyen SSL verifikáckó. Ezt a saját user-ünk alatt kell futtatni:
$ git config --global http.sslVerify false
Válasszuk a Repository -> Clone lehetőséget.
Kattintsunk a Repository URL melletti mező kis fekete nyílra, majd válasszuk az Add hosting provider lehetőséget.
Itt válasszuk a BitBucket server (Atlassian Stash) lehetőséget.
Adjuk meg a felhasználó nevünket és a jelszót, és a szerver base URL-jét, vagyis csak a hoszt nevet és a portot (ha nem szabványos a port):
Ekkor be fog jelentkezni a SmartGit a Bitbucket-be és az összes projektet ki fogja listázni, amihez van hozzáférésünk:
Nincs más dolgunk, mint hogy kiválasszuk a szükséges repo-t a listáról.
A jövőben ez a BitBucket provider azonnal elérhető lesz, nem lesz rá szükség hogy a szerveren kiválasszuk hogy mit akarunk klónozni, csak a Repository URL melletti kis fekete nyílra kell kattintani, és a lenyíló listában a BitBucket server-t kell választani, és a megnyíló ablakban már válogathatunk az elérhető repository-k között.
A következő oldalon adjuk meg a felhasználó nevet és a jelszót:
Válasszuk ki, hogy mit akarunk checkout-olni a kezdésként:
Majd válasszuk ki a lokális mappát a repositroy-nak. A SmartGit a korábbi választásaink alapján általában tökéletes ajánlatot fog adni:
A már felvett Hosting Provider-ek listáját megtekinthetjük az Edit -> Preferences -> Hosting Providers menüpont alatt:
Felület bemutatása
Branch-ek kezelése
Az oldal tején láthatjuk a repository választót.
Egyszerre mindig csak egy repository lehet kiválasztva, amin éppen dolgozunk. Nem lehet úgy repository-t váltani, hogy van módosított fájl a munkaterületen.
Az origin mutatja a távoli repository-t a Local Branches mutatja a helyi branch-eket. Az a lokális ág van jelenleg checkout-olva, amire a kis fekete nyíl mutat. (>)
Egyszerre mindig csak egy aktív branch lehet kiválasztva, amin éppen dolgozunk. Nem lehet úgy branch-et váltani, ha van módosított fájl a munkaterületen.
A képernyő alján láthatjuk az aktuálisan checkout-olt branchen hogy hol tart a HEAD pointer a lokális és a távoli repository-ban.
A sárga (origin) mutatja, hogy a távoli repository hol tart, és a zöld mutatja, hogy a lokális változatunkban hol tart a HEAD pointer. A zöld mezőbe bele van írva a branch neve is (a példában develop). Ha a sárga és a zöld mező egybe esik, akkor a lokális repoban ugyan az van mint a központiban. A távoli állapotot a rapidSVN onnan tudja (sárga mező) hogy nem PULL-ozta csak FETCH-elte a távoli tartalmat, vagyis anélkül hogy letöltötte volna a lokális repoba a változást, csak lekérdezte, hogy mi változott. A rapidSVN periodikusan magától is futtat FATCH-t.
Új branch létrehozása
Lokális branch létrehozása
Új branch létrehozásához checkout-oljuk azt a lokális branch-et amiből le akarunk ágazni, majd a fenti menüből válasszuk a Branch -> Add Branch lehetőséget.
Adjuk meg az új branch nevét, és válasszuk az "Add branch and checkout" lehetőséget. Ekkor lokálisan létrejön az új branch-ünk, ami checkout-olva is van. A távoli repóban (origin) ez a branch még nem létezik.
Kattintsunk az új lokális branch-re és nézzük meg a log-ját:
Az új branch-et a develop ágból hoztuk létre. Láthatjuk, hogy a HEAD-je egybe esik a develop ág HEAD-jével. Azoban az új branchnek (Sprint6-swagger-planning) nincs megfelelője a távoli repóban (sárga origin), mivel ez még csak egy lokális branch, ezt PUSH-olni kell hogy a távoliban is megjelenjen.
A fő képernyő alján, ahol az aktuálisan checkout-olt branch commit log-ját mutatja, láthatjuk, hogy a branch még csak lokálisan létezik, ezt jelzi a narancssárga vonal.
Lokális branch PUSH-olása
A lokális branch-et PUSH-olni kell a távoli branch-be. Jobb klikk a lokális branch-re, majd PUSH.
Ekkor a fő képernyőn lévő log historyban, ami az aktuálisan checkout-olt branch-et mutatja, a távoli sárga és a lokális zöld címke egy vonalba került, ezzel jelezve, hogy a lokális és a távoli branch már azonos.
Miután már bármit PUSH-oltunk az új branch-be, a log-ban látszik, hogy már külön életet él a develop-hoz képest:
A képen látható hogy a SPRINT6-swagger-planning branchben a lokális és a távoli ugyan azon a HEAD verzión van, mert a sárga és a zöld címke egybe esik. Azt is láthatjuk, hogy a pirossal jelölt develop ágban a lokális repónk le van maradva, mert a zöld lokális HEAD-et már több commit-al megelőzi a sárga távoli HEAD muató.
Pull Request-ek kezelése (BitBucket)
- Incoming pull: requests are those which other users are requesting to pull from their repositories. They are displayed in a separate category called Pull Requests in the Branches view.
- Outgoing pull: requests are those which you have sent to other users/repositories, requesting them to pull your changes. They are display directly below the local (or if it does not exist), the remote branch in the Branches view.
Pull request készítése
Pull request megtekintése
Checkout-olni kell azt a branch-et (vagy a master-t) aminek a pull request-jeit meg akarjuk nézni.
Láthatjuk, hogy a SmartGit jelzi is, hogy a master ágon vannak Pull request-ek.
A megnyíló log ablakban láthatjuk, hogy a PULL request-ek külön vannak gyűjtve. A példában 11 pull request van. Ahhoz hogy meg tudjuk tekinteni a PULL request tartalmát és az arra adott comment-eket, vagy hogy mi is commentelni tudjunk, le kell Fetch-elni kell a pull requestet. Kattintsunk rá a megtekinteni kívánt pull request nevére, majd válasszuk a Fetch pull request lehetőséget.
Ekkor a már lehozott pull request előtt meg fog jelenni egy Checkbox, ezzel jelezve, hogy már hozzá tudjuk adni ill ki tudjuk szedni az egyesített log nézetből.
Ha be van pipálva a checkbox a már lehozott (Fetch-elt) pull request sorában, akkor az külön megjelenik az egyesített log fában:
Ha volt hozzá komment is, akkor egy narancssárga kis buborék jelenik meg a neve mellett.
Válasszuk ki az egyesített log fában a pull request sorát:
Ekkor a bal oldali fájl listázóban meg fognak jelenni a Pull request-hez tartozó fájlok:
A narancssárga buborék jelzi, hogy az adott fájlhoz van comment.
Ha kiválasztunk egy fájlt, akkor az kommentekkel együtt meg fog jelenni a bal alsó ablakban:
A kommentekre lehet válaszolni, vagy a fájl tetszőleges pontjára új comment-et beszúrni.
Ha válaszolni akarunk, akkor jobb click a kommentre, majd Replay comment:
A megnyíló ablak ki lesz már eleve töltve az eredeti comment tartalmával, amire válaszolni akarunk. Ezt töröljük ki és írjuk be a felső mezőbe a választ:
A válasz meg fog jelenni az eredeti comment alatt:
Pull request frissítése
Ha egy commentezett pull request-ünket frissíteni akarunk, akkor nincs más dolgunk, mint hogy ugyan arra a branch-re PUSH-oljuk be ugyan azokat a fájlokat módosítva, amire a PULL request volt kérve. Ekkor a bitBucket észre fogja venni, és frissíteni fogja az eredeti request-et. Ha a PUSH után megnyitjuk a LOG-ját a repository-nak, akkor a pull request felsorolásban, látni fogjuk, hogy a lokális másolata a pull request-nek már elavult.
Az egyesített log fában láthatjuk, hogy a branch-ünk már egyel előrébb jár mint a pull request:
Külön frissíteni kell a pull request adatokat:
Ekkor láthatjuk az egyesített log fában, hogy megint a pull request kerül a commit-unk elé:
Innentől kezdve nem lehet megnézni az előző kommenteket.
A bitbucket-ben látható, hogy mind a két commit-ot hozzákapcsolta az eredeti pull request-hez:
Pull request elfogadása
Merge feature branch to master
Ha van egy branch-ünk amit egy másik branch tetejére akarunk rakni (tipikusan egy feature branch-et a develop-ra vagy master-re, akkor elsőként inden lokális változtatást push-olni kell a távoli szerverre, majd bele kell állni (checkout) abba az ágba, ahova merge-ölni akarjuk a feature branch-et, az esetünkben ez a develop branch lesz.
Nyissuk meg a log fát a develop ágon (jobb click -> logs). A bal oldali pipálós listában pipáljuk be azt a branch-et, amit mergelni szeretnénk a developre. Majd az egyesített log fában keressük meg. Mivel a develop-ban állunk, egy még nem merge-ölt feature branch vége a levegőben fog lógni:
Kattintsunk rá, hogy legyen kijelölve.
Ez után válasszuk a felső fő menüben a Merge lehetőséget. Ekkor a felugró ablakban szövegesen is ki lesz írva, hogy mit hova akarunk merge-ölni.
Két lehetőségünk van:
- Azonnal készítünk az egyesített állapotból egy úgynevezett merge-commit-ot. Ezt akkor tehetjük meg ha nincs conflict.
- Csak a munkaterületre merge-öli rá a smartGit a feature branch-et, és majd mi végezzük el a commit-ot miután feloldottuk a konfliktust.
Általában az első lehetőség megfelelő. Ez után láthatjuk majd, hogy a fő képernyőn a branch history-ban, hogy a lokális reponk megelőzi a távoli head-et.
Azt is láthatjuk, hogy automatikusan adott commit üzenetet a smartGit: "Merge remote-tracking branch 'origin/XXXX' into develop".
Most ha nyomunk egy PUSH-t, akkor fel is fogja küldeni az origin-ba az új merge-commit-ot.