Git branch – czym są gałęzie w systemi kontroli wersji?
Dawno nie było w kursie o samym Gicie – na samym początku pokazałem Ci podstawy Gitach byś potrafił wysyłać swoje zmiany na zdalne repozytorium. Jednak prawdopodobnie Twoja wiedza na temat Gita nie jest wystarczająca do pracy w zespole nad wspólnym projektem, a to jest główna siła tego narzędzia.
W takim razie w tym tygodniu postaram się Cię nauczyć pracować z Gitem przy użyciu wielu gałęzi czyli tak jak dzieje się to codziennie w każdej firmie programistycznej.
Czym jest branch?
Branch to nic innego jak gałąź – czyli taka odnoga naszego projektu, utworzona w danym czasie. Coś takiego jak kopia – może żyć swoim życiem i w każdej chwili może być dołączona do głównej gałęzi.
W końcu miałeś już do czynienia w Gicie z jedną gałęzią – a tą gałęzią jest właśnie master czyli główna gałąź repozytorium.
Branch może zostać utworzony tak naprawdę na podstawie każdej z innych gałęzi, lecz zazwyczaj się to robi na podstawie głównej gałęzi – master.
Po co te całe gałęzie?
Gałezię pozwalają w bardzo w prosty sposób pracować wielu osobom jednocześnie nad danym projektem. Przypuśćmy, że mamy duży projekt, szykuje się nowa wersja produktu – około 12 feature’ów, 5 programistów.
Dzięki branchom wszyscy programiści mogą pracować równolegle nie wchodząc sobie wcale w paradę – jak by to wyglądało?
Każdy programista na początek wziąłby jedno zadanie – jedna dodatkowa funkcjonalność i dla niej utworzyłem osobny branch odłączając się od gałęzi master. Po ukończeniu swojego zadania mógłby dołączyć swój branch do mastera i pracować nad kolejnym – łączeniem gałęzi zajmiemy się w kolejnej lekcji. Teraz skupmy się na branchach.
Jakieś komendy?
W tej lekcji będzie używać głównie dwóch komend:
Sprawdzenie dostępnych gałęzi w repozytorium:
git branch
Utworzenie nowego brancha
git branch <nazwa-brancha>
Przełączenie się na gałąź
git checkout <nazwa-brancha>
Przełączenie się na gałąź i utworzenie jeśli jeszcze nie istniała:
git checkout -b <nazwa-brancha>
Przyda nam się jeszcze jedna – do sprawdzenia historii commitów danego brancha
git log
I to tyle – nie musisz ich już teraz zapamiętywać, będę je wykorzystywał w trakcie dalszej części lekcji. Bez problemu wejdą Ci szybko do głowy. 😉
Init project
Na początku musimy stworzyć w ogóle jakieś repozytorium – przypuśćmy, że stworzymy naprawdę prosty projekt – stwórz zwykły projekt Javy oraz zainicjalizuj git repository komendą:
git init .
Gdy mamy już repozytorium możemy dodać do niego plik .gitignore, który definiuje pliki/foldery, które mają nie być brane pod uwagę podczas śledzenia zmian. Krótko mówiąc GIT się nimi w ogóle nie interesuje, a dodatkowo nie będą one wysyłane na zewnętrzne repozytorium.
Ignorowane są zazwyczaj zewnętrzne biblioteki, pliki wynikowe lub pliki utworzone przez IDE – nie ma co zaśmiecać głównego repozytorium rzeczami, które każdy sam może wygenerować w swoim środowisku.
Wystarczy utworzyć plik .gitignore w katalogu projektu i zapisać tam takie trzy linie:
/target /out /.idea
Dzięki temu folery target i out nie będą brane pod uwagę – są to katalogi wynikowe oraz .idea, który przechowuje informację o projekcie w IntelliJ Idea. Mając taki plik zapisujemy go i robimy klasycznie init commita.
Czyli musimy dodać wszystkie pliki i stworzyć z nich commita
git add * git commit -m "Init commit"
Jeśli chcesz może również dodać swojego zdalne repozytorium, jednak ja tego nie będę póki co robił – zrobię to na koniec lekcji, abyś mógł zobaczyć mój kod.
CreateUser Feature
Pierwszym naszym zadaniem jest wykonanie funkcjonalności createUser czyli możliwości stworzenia Usera w klasie Main – chcę, żeby przykład był naprawdę prosty. 😉
Na początku wylistujmy sobie dostępne branche:
git branch
I jak widać istnieje tylko master:
* master
Stwórzmy, więc nasz pierwszy branch – create-user
git branch create-user
I tym razem po wylistowaniu otrzymamy trochę dłuższą listę:
create-user * master
Widzisz, że gwiazdka jest tylko przy masterze – oznacza to, że aktualnie jesteśmy na gałęzi master!
Zmieńmy, więc branch na create-user
git checkout create-user
I powinniśmy otrzymać:
Switched to branch 'create-user'
I możemy rozpocząć prace na naszym featurem – stwórzmy prostą klasę User:
package pl.maniaq; public class User { private String name; private String lastname; private Integer age; public User(String name, String lastname, Integer age) { this.name = name; this.lastname = lastname; this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", lastname='" + lastname + '\'' + ", age=" + age + '}'; } }
Zakomitujmy zmiany:
git add * git commit -m "Create User class"
Dodajmy teraz w klasie Main, możliwość wczytania danych od użytkownika i utworzenia Usera – wykorzystamy po prostu dobrze znany Scanner:
package pl.maniaq; import java.util.Scanner; public class Main { Scanner scanner = new Scanner(System.in); public void createUser() { String name, lastname; Integer age; System.out.println("Type a name: "); name = scanner.next(); System.out.println("Type a lastname: "); lastname = scanner.next(); System.out.println("Type your age: "); age = scanner.nextInt(); User user = new User(name, lastname, age); System.out.println("Utworzono Usera: " + user.toString()); } public static void main(String[] args) { // write your code here } }
I ponownie zacommitujmy zmiany na naszej gałęzi:
git add * git commit -m "Implement create user method in main class"
Ale jak widać się pomyliłem – zapomniałem wywołać metody w metodzie main, teraz Main wygląda tak:
package pl.maniaq; import java.util.Scanner; public class Main { static Scanner scanner = new Scanner(System.in); public static void createUser() { String name, lastname; Integer age; System.out.println("Type a name: "); name = scanner.next(); System.out.println("Type a lastname: "); lastname = scanner.next(); System.out.println("Type your age: "); age = scanner.nextInt(); User user = new User(name, lastname, age); System.out.println("Utworzono Usera: " + user.toString()); } public static void main(String[] args) { createUser(); } }
I ponownie musimy zacommitować zmiany:
git add * git commit -m "Call createUser function in main method"
I teraz możemy wyświetlić sobie całą historię zmian na naszej gałęzi:
git log
I wtedy widzimy wszystkie nasze commity:
commit 2720852d2eb7afde997c907d990a8b4a8b14f3b8 Author: klimson <klimson@gitlab.com> Date: Tue Oct 2 19:34:12 2018 +0200 Call createUser function in main method commit ed4163a50658c78d7339a1b93fc05cc265ce2e93 Author: klimson <klimson@gitlab.com> Date: Tue Oct 2 19:32:00 2018 +0200 Implement create user method in main class commit 0514884fc4dd0d355c7e72da54655fa3b2e76e08 Author: klimson <klimson@gitlab.com> Date: Tue Oct 2 19:28:39 2018 +0200 Create User class commit e09ee4d2f7d3fc13bad8094b42d2cae758c08ad0 Author: klimson <klimson@gitlab.com> Date: Tue Oct 2 19:23:57 2018 +0200 Init commit
Calculatator Feature
Kolejnym naszym zadaniem jest stworzneie klasy odpowiedzialnej za podstawowe operacje matematyczne – prosty kalkulator.
W takim razie wracamy do gałęzi głównej, ponieważ od niej będziemy tworzyć nową gałąź
git checkout master
Oraz tworzymy nową gałąź feature/calculator – dobrze jest dodawać przedrostek np. feature/hotfix/release/bugfix, aby wiedzieć co jest celem tego brancha
git checkout -b feature/calculator
Tym razem użyliśmy komendy checkout z parametrem -b, aby utworzyć branch i automatycznie przejść do niego.
I jak widać automatycznie zostaliśmy przeniesieni do nowego brancha:
Switched to a new branch 'feature/calculator'
I mam teraz takie branche:
create-user * feature/calculator master
A nasza historia commitów wygląda aktualnie tak: (git log)
commit e09ee4d2f7d3fc13bad8094b42d2cae758c08ad0 Author: klimson <klimson@gitlab.com> Date: Tue Oct 2 19:23:57 2018 +0200 Init commit
Dlaczego tak? Ponieważ utworzyliśmy gałąź od głównej gałęzi, gdzie był wykonany tylko init commit – zmiany na branchu create-user w ogóle nas nie dotyczą, dopóki ich nie dołączymy.
W takim razie stwórzmy sobie prostą klasę obsługującą podstawowe operacje matematyczne:
package pl.maniaq; public class Calculator { public static Integer add(Integer numberOne, Integer numberTwo) { return numberOne + numberTwo; } public static Integer subtract(Integer numberOne, Integer numberTwo) { return numberOne - numberTwo; } public static Integer multiply(Integer numberOne, Integer numberTwo) { return numberOne * numberTwo; } public static Float divide(Integer numberOne, Integer numberTwo) { return numberOne / (float) numberTwo; } }
Zacommitujmy oczywiście zmiany:
git add * git commit -m "Create calculator class" -m "Class contains methods: add/subtract/multiply/divide"
I teraz użyjmy naszej klasy w Main – dwie liczb od użytkownika i wynik wszystkie operacji na ekran – nic trudnego.
package pl.maniaq; import java.util.Scanner; public class Main { static Scanner scanner = new Scanner(System.in); public static void calculate() { Integer x, y; System.out.println("Type first number: "); x = scanner.nextInt(); System.out.println("Type second number: "); y = scanner.nextInt(); System.out.println("Sum: " + Calculator.add(x, y)); System.out.println("Subtract: " + Calculator.subtract(x, y)); System.out.println("Multiply: " + Calculator.multiply(x, y)); System.out.println("Divide: " + Calculator.divide(x, y)); } public static void main(String[] args) { calculate(); } }
I oczywiście zacommitujmy naszą zmianę:
git add * git commit -m "Implement method calculate in main class" -m "Calculate method use Calculator class to calculating basic math operations"
I zróbmy teraz git log:
commit e86c8d90aa05187f7105f81a62c507a16851842f Author: klimson <klimson@gitlab.com> Date: Tue Oct 2 19:47:36 2018 +0200 Implement method calculate in main class Calculate method use Calculator class to calculating basic math operations commit 4431d049ae8dfeadb5446200d16d1d7c45a5d5b2 Author: klimson <klimson@gitlab.com> Date: Tue Oct 2 19:43:25 2018 +0200 Create calculator class Class contains methods: add/substract/multiply/divide commit e09ee4d2f7d3fc13bad8094b42d2cae758c08ad0 Author: klimson <klimson@gitlab.com> Date: Tue Oct 2 19:23:57 2018 +0200 Init commit
I jak widać nasze zmiany są widoczne na naszym branchu w historii commitów.
Na koniec
Na koniec wróćmy, więc do głównej gałęzi:
git checkout master
Oraz sprawdźmy ponownie historię mastera:
git log
I otrzymamy:
commit e09ee4d2f7d3fc13bad8094b42d2cae758c08ad0 Author: klimson <klimson@gitlab.com> Date: Tue Oct 2 19:23:57 2018 +0200 Init commit
I jak widać żadna zmiana na branchu nie wywołała zmian na masterze – czyli praca równoległa wielu programistów jest możliwa.
Jednak co teraz z tymi gałęziami?
Trzeba będzie je dołączyć do głównej gałęzi – jednak tym zajmiemy się już w następnej lekcji kursu!
Aby wypchnąć wszystkie swoje branche na zdalne repozytorium musisz użyć komendy:
git push origin --all
Dzięki temu zostaną wysłane wszystkie dostępne branche. 😉
Jeśli chcesz zobaczyć moje repozytorium utworzone w tej lekcji zerknij tutaj – możesz zobaczyć tam wszystkie commity oraz utworzone branche klikając po portalu GitHub.