W wielu tekstach na tym blogu znajdziecie instrukcje typu „pobierz z githuba” czy „sklonuj z githuba”. Oczywiście jeżeli macie cokolwiek wspólnego z programowaniem – git/github nie jest dla Was żadną tajemnicą. Ale jeżeli jesteście hobbystami – może warto przez chwilę zastanowić się, czym właściwie jest git, github – i jak go możecie wykorzystać do własnych celów?
Pisząc programy np. dla Arduino po prostu otwieramy projekt i wklepujemy kolejne linijki – zapisując plik od czasu do czasu. Problem polega na tym, że zachowując kolejną wersję kodu – tracimy tą poprzednią (nadpisujemy zawartość pliku). Najczęściej to jest ok, bo przecież kod dopiero piszemy, poprawiamy, zmieniamy. W którymś momencie możecie wpaść na jakiś pomysł, potem go odrzucić usuwając odpowiednie linie kodu. A potem może okazać się, że jednak pomysł był dobry. Używając „normalnego” systemu plików – zmiana jest stracona, musicie zaczynać od początku. Gorzej w przypadku, gdy zrobicie wiele zmian jednocześnie, które okażą się pogorszyć sytuację. Jeżeli używacie jednego pliku – przykro mi, powrót do początku będzie Was kosztował trochę czasu.
Dlatego większość z nas tworzy sobie kopie projektu w jego krytycznych momentach. I tak wkrótce macie szkice _01, _02, _03, _04. Po pewnym czasie jest ich mnóstwo i nie do końca wiadomo, co który zawiera.
git
Odpowiedzią jest git. git to takie oprogramowanie, które pomaga w wersjonowaniu kodu trzymając jego historię. Coś w stylu waszych _01, _02 – ale w znacznie bardziej usystematyzowany sposób i w w tym samym pliku (+dane trzymane przez git’a). W każdym momencie możecie wrócić do poprzedniej wersji pliku (lub całego projektu) – a nawet odczytać zostawione tam notatki.
Oczywiście git wymaga pewnej obsługi, tzn. oprócz zmian w Waszym kodzie – czasami musicie mu powiedzieć, że „coś” z tymi zmianami trzeba zrobić. Do dyspozycji macie narzędzie w linii komend – albo aplikacje graficzne. Aplikacje bardzo ułatwiają obsługę. Uważam jednak, że najpierw trzeba zrozumieć podstawy. Poniżej znajdziecie absolutne podstawy w wersji wywołań z linii komend.
git init
Tworzycie własne repozytorium. Repozytorium to taka baza danych Waszych plików. Możecie stworzyć repozytorium np. WszystkiProjektyArka, albo na potrzeby danego projektu (dobsonMotorizedBase). Wybór należy do Was. git’a to nie interesuje, dla niego to tylko pliki. Ba, nie interesują go nawet katalogi.
Repozytorium tworzycie w konkretnym miejscu, np. w folderze na waszym dysku twardym, na zdalnym komputerze albo w chmurze. Nie istnieją żadne magiczne rejestry i systemowe katalogi, chociaż git dokłada kilka własnych plików pomagających mu ogarnąć Wasze zmiany, np. trzymać ich historię. Katalog z repozytorium jest kompletny i niezależny – np. jeżeli skopiujecie go w inne miejsce na dysku – dalej będzie „działać”.
git clone
Jeżeli nie tworzycie repozytorium (lub jakieś już macie, albo chcecie „ściągnąć” stworzone przez kogoś innego np. na github), możecie je „sklonować”. To trochę więcej niż kopia, bo git zapamiętuje skąd pobraliście repozytorium i jaką jego wersję (a dokładniej wersję plików w tym repozytorium). Sklonować możecie z własnego dysku – albo np. z chmury (o github poniżej).
Klonując repozytorium tworzycie jego „kopię roboczą”. Możecie w niej robić, co Wam się podoba. Wasze zmiany nie zostaną wysłane do repozytorium, póki git’owi nie każecie tego zrobić. Przez zmiany rozumiem dodanie/usunięcie pliku, zmianę jego zawartości, przeniesienie w inne miejsce.
git add
git nie interesuje się plikami, póki nie każecie mu na nie popatrzyć. Dodając plik komendą „add”, git zaczyna go „śledzić” – a raczej patrzeć, jakie zmiany w nim robicie. Dodajecie pliki, git nie rozważa katalogu jako osobny element – tylko jako ścieżkę do pliku.
Pliki dodane do git’a, lądują w tzw. indeksie, scenie – ale jeszcze nie w samym repozytorium. Po dodaniu pliku dalej możecie go zmieniać. przenosić itp.
git mv
Jeżeli chcecie przenieść plik z jednego katalogu do drugiego lub zmienić jego nazwę:
- Dla pliku, który jeszcze nie został dodany do indeksu (przed git add) – operacje możecie wykonywać bez ograniczeń,
- Dla pliku już dodanego do indeksu: generalnie git powinien wykryć przeniesienie go w nowe miejsce automatycznie (na podstawie podobieństwa zawartości plików), ale bezpieczniejszym sposobem jest użycie komendy „mv”,
- Jeżeli nadpisujecie plik już istniejący w katalogu docelowym, może przydać się „mv –force”
git commit
Wiecie, że zmiana, którą zrobiliście, jest warta zapamiętania. „commit” jest jak stworzenie kolejnej wersji pliku _02, _03… Wydając tą komendę, git zaznacza sobie obecną zawartość danego pliku jako jego kolejną wersję.
Wydanie komendy 'commit’ nie wysyła jeszcze pliku do repozytorium (logicznie, w przypadku repozytorium lokalnego – lub fizycznie, w przypadku chmury). Zanim „wypchniecie” zmiany do repozytorium, możecie wiele razy wydać polecenie 'commit’ zaznaczając sobie jakieś konkretne punkty, w których jesteście.
Każdy 'commit’ wymaga podania komentarza. Jeżeli będziecie robili to uczciwie – za każdym razem będzie wiedzieli dlaczego stworzyliście konkretną wersję. To plus różnice między kolejnymi wersjami dadzą Wam pełen obraz zmian.
Po wydaniu komendy 'commit’, git uznaje daną wersję pliku za najnowszą (head).
git push
Zmiany gotowe, możecie je „wypchnąć’ (’push’) do repozytorium. Służy do tego komenda:
1 |
git push origin master |
Mówi ona mniej więcej:
- git push: wypchnij zmiany
- origin: do repozytorium, z którego pochodziły,
- master: na gałąź „master” – główną.
Ostanie (gałąź master) wymaga wyjaśnienia: kod jest rozwijany w „gałęziach” (branches). Programiści mogą tworzyć sobie gałęzie na własne potrzeby – np. rozwijania konkretnej funkcji lub po prostu eksperymentów. To tak jakby w jednym czasie istniało równocześnie wiele wersji pliku – z różną zawartością. Ta najlepsza, albo najbardziej przydatna (albo… – możliwości jest wiele) trafia do „głównej gałęzi” (pnia?), który nazywa się master (choć to tylko nazwa domyślna, właściwie może być dowolnie zmieniona). Stąd możecie usłyszeć: „pracujemy tylko na masterze” – gdy programiści po prostu tworzą kolejne wersje pliku w kopi roboczej mastera. Druga opcja to „mój kod jest na branchu” – gdy programiści „odbijają” wersję pliku na osobną gałąź i tam tworzą jej kolejne wersje. W jakimś momencie mogą zdecydować dodać kod do master’a – łącząc go z tym, co już tam jest (merge). Z czasem oczywiście drzewo takich gałęzi może przypominać zły sen Spidermana – i tu wchodzą do gry profesjonalne standardy pracy:) (ale to już całkiem inna historia).
git pull
„push” służy do wypchnięcia zmian do repozytorium. „pull” służy do pobrania zmian z repozytorium. Wyobraźcie sobie sytuację, gdzie macie kopię projektu na dysku, podobnie jak Wasz kolega na swoim komputerze. Kolega zrobił zmianę w pliku – powiedzmy – readme.txt, commit, push – i już jest w repozytorium. Teraz plik readme.txt na Waszym dysku jest w innej wersji niż w repozytorium – brakuje mu zmian zrobinych przez Waszego kolegę. Do tego służy „pull” – ściągnie zmiany z repozytorium i połączy je do Waszego kodu (merge).
I tu uwaga – czasami „merge” może nie być banalny. Zwłaszcza, jeżeli Wy i Wasz kolega pracowaliście nad jednym plikiem – ba, nad jedną linijką w nim. Wtedy może wymagać to trochę pracy. Ale jeżeli plik był zmieniony tylko przez jedną osobę – wtedy nie ma problemu, stanie się to automatycznie.
git fetch
git pull próbuje zsynchronizować pliki między gałęziami – pobiera je ze źródłowego repozytorium, łączy itp. fetch nie jest tak inwazyjny – pobiera dane, ale nie zmieni Waszej kopii roboczej. W tym sensie jest „bezpieczny” – ni namiesza Wam w plikach. W zasadzie „git pull” to po kolei „git fetch” i „git merge”.
git log
Polecenie log samo w sobie nie wykonuje żadnej akcji na plikach czy repozytorium, ale pokazuje Wam co się z nimi działo/dzieje.
git tag
Wspomnę jeszcze o tag: pozwala on nadać jakąś nazwę obecnej wersji pliku albo repozytorium, no. release 1.0.0. Nie będę go tu opisywał.
Więcej…
Oczywiście tych poleceń jest duuuużo więcej. git to potężne narzędzie, które staje się teraz podstawowym dla funkcjonowania prawie każdej firmy związanej w jakikolwiek sposób z oprogramowaniem. Zrozumienie powyższych właściwie wystarczy Wam do normalnej pracy z git.
Co może się jeszcze przydać:
Pytanie | Odpowiedź |
---|---|
Zrobiłem zmiany… ale kompletnie mi nie wyszły. Zmiany są tylko w kopii roboczej, nie zostały jeszcze „commit”. | git reset –hard HEAD |
Sprawdzanie statusu | git status git status -s |
Jakie branche są w repozyrorium, na jakim branchu jestem | git branch -a |
Wizualizacja zmian | git log –graph |
Stworzenie nowego brancha | git checkout -b <nazwa> |
Jakie zobaczyć różnice – np. przed mergem | git diff <source branch> <destination branch> |
Środowisko graficzne? | gitk |
github
Ktoś wpadł na pomysł, żeby zainstalować git’a w chmurze – i powstał github. github to nic więcej jak usługa chmurowa do której możecie wysyłać Wasz kod tak jak do lokalnego repozytorium git’a. Z tym że teraz możecie się dzielić kodem z kolegami. Po drugie – macie kopie swojego kodu gdzieś w sieci. Gdyby coś się stało z Waszym lokalnym kodem – nadal będziecie mieli go na github.
Rejestracja na github jest darmowa, choć są pewne ograniczenia – np. w ilości dostępnego miejsca lub ilości dziennych transakcji. Dla hobbystów te limity właściwie się nie liczą.
Praktycznie
Mój git jest pod adresem: https://github.com/arekmerta. Na potrzeby projektu teleskopu założyłem nowe repozytorium 'dobsonMotorized base:
Nowe repo pojawiło się pod adresem: https://github.com/arekmerta/dobsonMotorizedBase. Teraz założyłem w nim podkatalogi dla Arduino i ESP32. Jak wspominałem: git działa tylko na plikach. Nie traktuje katalogu jako osobny obiekt – ale po prostu ścieżkę do pliku. Jeżeli więc chcecie założyć jakiś katalog, musicie stworzyć w nim plik. README.md jest zawsze dobrym pomysłem. Kliknijcie 'create new file’ i podajcie ścieżkę do pliku:
U dołu strony znajdziecie przyciski dodania pliku do repozytorium – pamiętajcie o komentarzu:
To samo zrobię dla ESP32. Teraz github wyglada tak:
Dodawanie plików
Wracając na mój komputer, sklonuję nowo stworzone repozytorium z chmury:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$git clone https://github.com/arekmerta/dobsonMotorizedBase.git Cloning into 'dobsonMotorizedBase'... remote: Enumerating objects: 14, done. remote: Counting objects: 100% (14/14), done. remote: Compressing objects: 100% (9/9), done. remote: Total 14 (delta 1), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (14/14), done. $cd dobsonMotorizedBase $ tree . ├── arduino │ └── README.md ├── esp32 │ └── README.md └── README.md 2 directories, 3 files |
Mój dotychczasowy kod dla esp32 rozwijałem w innym katalogu. Skopiowałem projekt 'esp32_client.ino’ do odpowiedniego katalogu w drzewie repozytorium. Struktura zmieniła się:
1 2 3 4 5 6 7 8 9 10 11 |
$ tree . ├── arduino │ └── README.md ├── esp32 │ ├── esp32_client │ │ └── esp32_client.ino │ └── README.md └── README.md 3 directories, 4 files |
Dodam projekt ./esp32_client/esp_client.ino to gita i wypchnę go do chmury:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ git add ./esp32/esp32_client/esp32_client.ino $ git commit [master 17dd5d5] Initial version of the esp32_client added. 1 file changed, 480 insertions(+) create mode 100644 esp32/esp32_client/esp32_client.ino $ git push origin master Username for 'https://github.com': arekmerta Password for 'https://arekmerta@github.com': Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (5/5), 3.91 KiB | 1.30 MiB/s, done. Total 5 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), completed with 1 local object. To https://github.com/arekmerta/dobsonMotorizedBase.git 8fdb0e6..17dd5d5 master -> master |
Zauważcie, że jeżeli teraz znowu zrobię 'commit’, git podpowie mi, że nie ma żadnych zmian – i nie ma co commitować:
1 2 3 4 5 |
$ git commit On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean |
Gdy tylko zrobię zmiany w jakimś pliku:
1 2 3 4 5 6 7 8 |
$ git commit On branch master Your branch is up to date with 'origin/master'. Changes not staged for commit: modified: esp32/README.md no changes added to commit |
Git pokaże mi, że mam zmiany – ale nie powiedziałem mu jeszcze, żeby je uwzględnił (stąd: 'no changes to commit’). Najłatwiej zrobić to:
1 2 3 |
git commit -a [master db08d82] Changed readme file - just some more explanations. 1 file changed, 9 insertions(+), 1 deletion(-) |
Oczywiście zmiany znajdą się na github dopiero, gdy użyjecie komendę „push”.
Teraz: zrobiłem więcej zmian w projekcie i dodałem nowy plik 'esp32/esp32_client/joystick.cpp’:
1 2 3 4 5 6 7 8 9 10 11 12 |
$ git commit On branch master Your branch is up to date with 'origin/master'. Changes not staged for commit: modified: esp32/README.md modified: esp32/esp32_client/esp32_client.ino Untracked files: esp32/esp32_client/joystick.cpp no changes added to commit |
Znowu mam 'no changes to commit, chociaż git zauważył zmiany w dwóch plikach i jeden nowy plik. Naprawmy to:
1 2 3 4 5 |
$ git add ./esp32/esp32_client/joystick.cpp $ git commit -a [master a50952f] Added class for manipylating joystick. 3 files changed, 114 insertions(+), 13 deletions(-) create mode 100644 esp32/esp32_client/joystick.cpp |
Zmiany na zewnątrz
A co w wypadku, gdy np. przeedytujecie plik README.md przez stronę githuba? Ano trzeba zsynchronizować zdalne repo z lokalym.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ git pull origin master ... remote: Enumerating objects: 7, done. remote: Counting objects: 100% (7/7), done. remote: Compressing objects: 100% (4/4), done. remote: Total 4 (delta 1), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (4/4), done. From github.com:arekmerta/dobsonMotorizedBase * branch master -> FETCH_HEAD a50952f..66461f0 master -> origin/master Updating a50952f..66461f0 Fast-forward esp32/README.md | 28 ++++++++++++++++++---------- |
Hasło do pusha
Za każdym razem, gdy będziecie robili 'git push’ do zdalnego repozytorium, git będzie prosił Was o użytkownika i hasło. Trochę to uciążliwe. Chyba najłatwiejszy sposób na rozwiązanie tego problemu jest używanie klucza ssh przy pushu. Pod linuks:
1 2 |
$ cd ~/.ssh $ ssh-keygen -t rsa |
Teraz w katalogu ~/.ssh znajdziecie pliki kluczy id_rsa (prywatny) i id_rsa.pub (publiczny) Na stronie github dodajcie klucz publiczny w menu/settings/SSH and GPG keys.
I dodaję klucz do konfiguracji git’a:
1 2 3 4 5 6 |
$ eval "$(ssh-agent -s)" $ ssh-add ~/.ssh/id_rsa Enter passphrase for /home/arek/.ssh/id_rsa: Identity added: /home/arek/.ssh/id_rsa (/home/arek/.ssh/id_rsa) $ git remote set-url origin git@github.com:arekmerta/dobsonMotorizedBase.git $ git push origin master |
Teraz powinno się już udać bez konieczności podania hasła, ale…
Git push – passphrase
Git przestał pytać o uzytkownika i hasło – ale za to pyta o passphrase. Rozwiązanie:
1 2 3 4 5 6 7 |
$ nano ~/.ssh/config Host * AddKeysToAgent yes IdentityFile ~/.ssh/id_rsa [Ctrl-X, Y, Enter] $ eval $(ssh-agent) $ ssh-add |
Dodatek: format 'md’ (markdown)
Format 'md’ (wspomniany plik README.md) to plik tekstowy z pewnymi znakami sterującymi, które są interpretowane, np:
- ’#’, '#’ – nagłówek, np. '# to jest nagłówek’
- ’*’ – kursywa, np.: '*to będzie napisane kursywą*’
- ’**’ – pogrubienie, np.: '**to będzie pogrubione**’
- ’*’ element listy (tylko jedna gwiazdka),
- ’1′ elementy listy numerowanej,
- [GitHub](http://github.com) – link
- Tabelka:
1234Head 1 | Head 2-------|-------Elem 0 | Elem 1Elem 2 | Elem 3
Dla przykładu:
1 2 3 4 5 6 7 8 9 |
# What is it? This is **ESP32 part** of the *motorized dobson* stand. I'm using ESP32 Wroom module. # Derived from? This project is using: * https://github.com/espressif/arduino-esp32 * https://circuits4you.com/2018/12/31/esp32-hardware-serial2-example/ * https://github.com/johnrickman/LiquidCrystal_I2C * https://github.com/adafruit/Adafruit_GPS |
Jeżeli chcecie przetestować zmiany możecie użyć np. atom’a.
1 2 3 4 |
$ sudo add-apt-repository ppa:webupd8team/atom $ sudo apt update $ sudo apt install atom $ atom README.md |
Źródła
- https://rogerdudler.github.io/git-guide/index.pl.html
- https://stackoverflow.com/questions/8588768/how-do-i-avoid-the-specification-of-the-username-and-password-at-every-git-push/36955408
- https://help.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh
- https://kamarada.github.io/en/2019/07/14/using-git-with-ssh-keys/#.XrmvMhZS_Q0
- https://koukia.ca/rename-or-move-files-in-git-e7259bf5a0b7
- https://guides.github.com/features/mastering-markdown/
- https://unix.stackexchange.com/questions/12195/how-to-avoid-being-asked-passphrase-each-time-i-push-to-bitbucket