Sziasztok!
Git…git…git… Néhány feature-t megnéztünk már a Git-et illetően, de bízzatok bennem, sok hiányzik még. Nem feltétlenül kritikus feature-ök. Ismertem olyan szakmabelit, aki gyakorlatilag 4-5 parancs segítségével évekig használta a git-et. Természetesen ritkán dolgozott csapatban, és amikor így tett, akkor segítettünk neki feloldani a problémákat, de ettől a tény tény maradt. Én híve vagyok annak, hogy egyszerűsítsük az életünket ahol csak lehet, ha ez nem így lenne akkor Java-ban még mindig használnám a Vector-t, és verziókezelőnek még mindig a CVS-t. De nem csak az egyszerűséget szeretem, hanem a teljességet is. Amikor „pillanatnyilag” megoldhatatlannak tűnő problémám van, akkor általában két irányba tudok elindulni, az egyik eset amikor gyorsan kell reagálnom, ekkor 2-3 perc sikertelen kutatás/olvasgatás után megkeresem a legrelevánsabb embert a problémám feloldására és vagy „páros programozás”-sal vagy egyszerű szellemi eszmecserével megoldjuk a problémát. A másik eset, amikor nem kritikus a probléma és a reakció eltolódhat. Ekkor van az, hogy a kutatás/olvasgatás nem 2-3 perc, hanem a probléma megoldásáig tart. Miért fontos ez? Ezek azok az esetek amikor újabb tudást van lehetőségem integrálni a szervezetembe. Minden ilyen alkalommal mélyen gyarapszik a tudásom, és a piac számára értékesebb leszek. Arról nem is beszélve, hogy a következő hasonló esetben már én leszek az előbb említett releváns ember más számára, illetve ha velem jön szembe a probléma, akkor hatékonyan tudom megoldani. Hatékonyság, Produktivitás. Két igen fontos része a fejlesztő életének.
De kicsit visszakanyarodva a git-hez: egy csapat amikor együtt dolgozik, szinte elkerülhetetlen, hogy valaki előbb-utóbb ne csesszen el valamit. Általában ilyenkor olyan előre nem látható baki következik be, amire alapvetően nem számítasz, és csak azon múlik, hogy mennyi munkaórát pazaroltok el a kijavításra, hogy mennyi tudásotok van. És itt bizony már fontos szerepet játszik, hogy minél nagyobb tudással kezelitek a git-et, annál kevesebb esélyt adtok a hihetetlen bakiknak. :)
Stash
Adott az alábbi helyzet: többen dolgoztok ugyanazon a branch-en, és vélhetőleg ez nem a master kell, hogy legyen. :) (persze a probléma oldaláról szemlélve teljesen mindegy, hogy hogyan hívjuk a branch-et, inkább branch szervezési oldalról lesz fontos, hogy ez ne a master legyen, de erről talán máskor. ;-) )
Mivel egy adott branch-en dolgoztok, ezért ez azt feltételezi, hogy kvázi ugyanazon az kódbázison operáltok, és röpködnek a commitok a repository-ba. Könnyen belátható az is, hogy ilyen esetekben ütközések fordulnak elő. Kérdésem a következő: mit teszel, ha te éppen folyamatában vagy egy feature fejlesztésének, amikor hirtelen megszólal a kollégád, hogy kész vagyok, feltoltam, használhatod? Mivel elég nagy annak az esélye, hogy ütköznek a kódok, így attól tekintsük el, hogy kihúzod a kódot, nincs ütközés és mindenki boldog. Nagyobb a valószínűsége ugye annak, hogy kihúzod a kódot és van ütközés. Ekkor a git megkér, hogy commitolj, hiszen addig nem tudsz az új kódhoz jutni. Nézzük, mik azok az esetek amik hirtelen eszembe jutnak a „mit teszel” kérdés megválaszolására:
-
Az ütköző fájlok tartalmát kimásolod valahová, hogy megmaradjanak neked, majd ezen fájlok tartalmát visszaállítod az eredetire, hogy ne legyen merge conflict. (Ezt soha ne csináld, időigényes, tele van hibalehetőséggel, és arról árulkodik, hogy gányolsz. :) )
-
Teszel egy commitot, aminek valami bullshit lesz a message-e, hiszen ez egy kényszer cselekvés, amit azért teszel, hogy használhasd más kódját. (Esetleg megpróbálod eltüntetni a nyomokat, de nagyobb val’séggel ottmarad a commitod mint „szemét”.)
-
Külön branch-et készítesz, és ott próbálod el ugyanezt, majd valamilyen módon igyekszel eltüntetni a nyomokat, és így az eredményben nem fog látszódni a „++” commit message. :)
-
Használod a Stash-t.
Nagyvonalakban mit fogok csinálni: Nem fogok imitálni több felhasználót, ugyanis azt majd az élet megteszi helyettem. :)
-
Én inkább készítek egy repository-t,
-
a master branch-en létrehozok egy fájlt, legyen ez a bonyolultság kedvéért a HelloWorld.java :),
-
implementálom a csoda funkcionalitást,
-
majd commitolok.
-
Ezek után létrehozok egy másik branch-et.
-
Ezen a branch-en módosítom a fájlt,
-
majd commitolok.(Ez az a lépés, amellyel helyettesítem a másik fejlesztőt.) Ha ezzel kész vagyok,
-
visszamegyek a master branch-re, és úgy döntök, hogy
-
módosítom az implementációt. Amint módosítottam, de még nem vagyok kész (nem hangzik el commit):
-
akkor támad az a gondolatom, hogy mergelem a másik branch-en módosított fájlt a masteren található módosítás közbeni állapotú fájlommal. (Ez az a lépés, amikor a másik fejlesztő felkiált, hogy kész van, használhatom a kódját.) Nem hajtja végre a merge-t, abortál, így használjuk a stash-t.
-
Újra megpróbáljuk a merge-t, de ezúttal sikeresen.
-
Kivesszük a stash-ból a verziónkat, ám itt figyelmeztet minket a git, hogy merge conflict van.
-
A korábban tanult módon feloldjuk a conflict-ot,
-
Megnézzük, hogy mi történt a git log-ban, és örülünk :)
Akkor nézzük meg ezt a gyakorlatban:
1-4:
$ git init
Initialized empty Git repository in /tmp/stash/.git/
$ git init
Reinitialized existing Git repository in /tmp/stash/.git/
$ touch HelloWorld.java
$ vi HelloWorld.java
$ cat HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
$ git add HelloWorld.java
$ git commit -m "HelloWorld.java"
[master (root-commit) a92b95e] HelloWorld.java
1 file changed, 5 insertions(+)
create mode 100644 HelloWorld.java
$
Eddig semmi érdekes nem történt, menjünk is tovább:
5-7:
$ git checkout -b new_branch
Switched to a new branch 'new_branch'
$ vi HelloWorld.java
$ cat HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HELLO WORLD");
}
}
$ git commit -a -m "HelloWorld.java"
[new_branch f3f3339] HelloWorld.java
1 file changed, 1 insertion(+), 1 deletion(-)
$
Itt „Hello World” – ről módosítottam „HELLO WORLD” – re.
8-9:
$ git checkout master
Switched to branch 'master'
$ vi HelloWorld.java
$ cat HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
$
Ezen lépésben: „Hello World”-ről módosítottam „Hello, World!”-re.
Nézzük meg a master és a new_branch-en található HelloWorld.java verziók közötti különbséget:
$ git reflog
a92b95e HEAD@{0}: checkout: moving from new_branch to master
f3f3339 HEAD@{1}: commit: HelloWorld.java
a92b95e HEAD@{2}: checkout: moving from master to new_branch
a92b95e HEAD@{3}: commit (initial): HelloWorld.java
$ git diff f3f3339
diff --git a/HelloWorld.java b/HelloWorld.java
index 836fb20..db1db64 100644
--- a/HelloWorld.java
+++ b/HelloWorld.java
@@ -1,5 +1,5 @@
public class HelloWorld {
public static void main(String[] args) {
- System.out.println("HELLO WORLD");
+ System.out.println("Hello, World!");
}
}
Csodás. :) Szépen látható, hogy a new_branch-en a módosított szöveg nem egyezik a master branch-en található módosított szöveggel.
Még egy utólagos állapot ellenőrzés:
Master branch-en vagyunk-e?
$ git branch
* master
new_branch
$
Ugye nem commitoltuk be a HelloWorld új változatát?
$ git status
# On branch master
# Changes not staged for commit:
# (use "git add ..." to update what will be committed)
# (use "git checkout -- ..." to discard changes in working directory)
#
# modified: HelloWorld.java
#
no changes added to commit (use "git add" and/or "git commit -a")
$
Ugye az a fájl tartalma megegyezik azzal, amit a diff is mutat?
$ cat HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
$
Mivel mindenhol igen a válasz, ezért jöhet a következő lépés:
(Ezek a környezetkialakító lépések voltak, nem túl izgisek, nem túl érdekesek, szóval nézzük meg a lényegi részét a story-nak. :)
10: ekkor támad az a gondolatom, hogy mergelem a másik branch-en módosított fájlt a masteren található módosítás közbeni állapotú fájlommal. (Ez az a lépés, amikor a másik fejlesztő felkiált, hogy kész van, használhatom a kódját.) Nem hajtja végre a merge-t, abortál, így használjuk a stash-t.
$ git merge new_branch
Updating a92b95e..f3f3339
error: Your local changes to the following files would be overwritten by merge:
HelloWorld.java
Please, commit your changes or stash them before you can merge.
Aborting
$
Nem engedi nekünk a merge-t, hiszen nem commitáltuk az új verziónkat, ezzel ő nem tud mit kezdeni, és nem is akar.
$ git stash
Saved working directory and index state WIP on master: a92b95e HelloWorld.java
HEAD is now at a92b95e HelloWorld.java
$
Közbevetett ellenőrzés:
$ git status
# On branch master
nothing to commit (working directory clean)
$
Tehát nincs ott a módosított verziónk, mint modified állapotú fájl.
$ git stash list
stash@{0}: WIP on master: a92b95e HelloWorld.java
$
Cserébe, az elvárt eredmény: sikeresen bekerült a stack-be.
11: Újra megpróbáljuk a merge-t, de ezúttal sikeresen.
$ git merge new_branch
Updating a92b95e..f3f3339
Fast-forward
HelloWorld.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ cat HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HELLO WORLD");
}
}
$
12: Kivesszük a stash-ból a verziónkat, ám itt figyelmeztet minket a git, hogy merge conflict van.
$ git stash pop
Auto-merging HelloWorld.java
CONFLICT (content): Merge conflict in HelloWorld.java
$
13: A korábban tanult módon feloldjuk a conflict-ot,
$ git mergetool
Merging:
HelloWorld.java
Normal merge conflict for 'HelloWorld.java':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (kdiff3):
$
Az alábbi verziót kreáltam a merge kapcsán:
$ cat HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
$
14: Megnézzük, hogy mi történt a git log-ban, és örülünk :)
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: HelloWorld.java
#
$ git reflog
f3f3339 HEAD@{0}: merge new_branch: Fast-forward
a92b95e HEAD@{1}: checkout: moving from new_branch to master
f3f3339 HEAD@{2}: commit: HelloWorld.java
a92b95e HEAD@{3}: checkout: moving from master to new_branch
a92b95e HEAD@{4}: commit (initial): HelloWorld.java
$
Eredmény: nincs fölösleges commit, a másik fejlesztő által létrehozott módosításokat nekem szimpatikus módon tudtam merge-lni a saját fejlesztésemhez, és mindezt elég egyszerűen. Most ezt így visszagörgetve lehet hosszúnak tűnik, de a valóságban a 10.-es ponttól indul a játék, a többi csak a bemutatás végett van itt: így aztán a bejegyzés hossza ne rettentsen el semmi jótól. ;-)