From 6db3eb427cc6a6196de33760065e4fc629d32fae Mon Sep 17 00:00:00 2001 From: damianmichna Date: Wed, 3 Jul 2013 22:17:52 +0200 Subject: [PATCH] first quick and dirty translation from german translation using omegat --- Makefile | 2 +- book.css | 311 +++++++++++++++----------- de/Makefile | 0 pl/basic.txt | 404 +++++++++++++++++----------------- pl/branch.txt | 435 ++++++++++++++++--------------------- pl/clone.txt | 412 +++++++++++++++++------------------ pl/drawbacks.txt | 188 ++++++++-------- pl/grandmaster.txt | 411 +++++++++++++++-------------------- pl/history.txt | 455 +++++++++++++++++---------------------- pl/intro.txt | 116 +++++----- pl/multiplayer.txt | 400 ++++++++++++++-------------------- pl/preface.txt | 123 +++++------ pl/secrets.txt | 360 +++++++++++++------------------ pl/translate.txt | 54 ++--- szl/basic.txt | 196 +++++++++++++++++ szl/branch.txt | 190 ++++++++++++++++ szl/clone.txt | 194 +++++++++++++++++ szl/drawbacks.txt | 91 ++++++++ szl/grandmaster.txt | 179 +++++++++++++++ szl/history.txt | 198 +++++++++++++++++ szl/intro.txt | 57 +++++ szl/multiplayer.txt | 169 +++++++++++++++ {pl => szl}/preface.txt | 44 ++-- szl/secrets.txt | 146 +++++++++++++ szl/translate.txt | 21 ++ tmp/basic.txt | 195 +++++++++++++++++ tmp/branch.txt | 190 ++++++++++++++++ tmp/clone.txt | 194 +++++++++++++++++ tmp/drawbacks.txt | 91 ++++++++ tmp/grandmaster.txt | 179 +++++++++++++++ tmp/history.txt | 197 +++++++++++++++++ tmp/intro.txt | 57 +++++ tmp/multiplayer.txt | 163 ++++++++++++++ {pl => tmp/orig}/basic.txt | 0 {pl => tmp/orig}/branch.txt | 0 {pl => tmp/orig}/clone.txt | 0 {pl => tmp/orig}/drawbacks.txt | 0 {pl => tmp/orig}/grandmaster.txt | 0 {pl => tmp/orig}/history.txt | 0 {pl => tmp/orig}/intro.txt | 0 {pl => tmp/orig}/multiplayer.txt | 0 {pl => tmp/orig}/preface.txt | 0 {pl => tmp/orig}/secrets.txt | 0 {pl => tmp/orig}/translate.txt | 0 tmp/preface.txt | 45 ++++ tmp/secrets.txt | 134 ++++++++++++ tmp/translate.txt | 17 ++ 47 files changed, 4604 insertions(+), 2014 deletions(-) rewrite book.css (99%) rewrite de/Makefile (100%) mode change 120000 => 100644 rewrite pl/basic.txt (92%) rewrite pl/branch.txt (97%) rewrite pl/clone.txt (94%) rewrite pl/drawbacks.txt (98%) rewrite pl/grandmaster.txt (95%) rewrite pl/history.txt (89%) rewrite pl/intro.txt (99%) rewrite pl/multiplayer.txt (98%) rewrite pl/preface.txt (86%) rewrite pl/secrets.txt (97%) rewrite pl/translate.txt (99%) create mode 100644 szl/basic.txt create mode 100644 szl/branch.txt create mode 100644 szl/clone.txt create mode 100644 szl/drawbacks.txt create mode 100644 szl/grandmaster.txt create mode 100644 szl/history.txt create mode 100644 szl/intro.txt create mode 100644 szl/multiplayer.txt copy {pl => szl}/preface.txt (52%) create mode 100644 szl/secrets.txt create mode 100644 szl/translate.txt create mode 100644 tmp/basic.txt create mode 100644 tmp/branch.txt create mode 100644 tmp/clone.txt create mode 100644 tmp/drawbacks.txt create mode 100644 tmp/grandmaster.txt create mode 100644 tmp/history.txt create mode 100644 tmp/intro.txt create mode 100644 tmp/multiplayer.txt copy {pl => tmp/orig}/basic.txt (100%) copy {pl => tmp/orig}/branch.txt (100%) copy {pl => tmp/orig}/clone.txt (100%) copy {pl => tmp/orig}/drawbacks.txt (100%) copy {pl => tmp/orig}/grandmaster.txt (100%) copy {pl => tmp/orig}/history.txt (100%) copy {pl => tmp/orig}/intro.txt (100%) copy {pl => tmp/orig}/multiplayer.txt (100%) copy {pl => tmp/orig}/preface.txt (100%) copy {pl => tmp/orig}/secrets.txt (100%) copy {pl => tmp/orig}/translate.txt (100%) create mode 100644 tmp/preface.txt create mode 100644 tmp/secrets.txt create mode 100644 tmp/translate.txt diff --git a/Makefile b/Makefile index 0ca17f7..aa15be7 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ # # For now, I've uploaded a PDF to the website; it was supplied by # Trần Ngọc Quân who used OpenOffice to convert HTML to PDF. -TRANSLATIONS = de es fr ru uk vi zh_cn zh_tw it +TRANSLATIONS = de es fr ru uk vi zh_cn zh_tw it pl szl LANGS = en $(TRANSLATIONS) SHELL := /bin/bash diff --git a/book.css b/book.css dissimilarity index 99% index ed2fb1a..7f6d06f 100644 --- a/book.css +++ b/book.css @@ -1,124 +1,187 @@ -body { - font-size: 90%; - font-family: verdana, arial, sans-serif; -} - -tt, code, pre, .type { - font-family: andale mono, courier new, courier, monospace; - font-size: 90%; -} - -pre { - color: #0000aa; -} - -ul li p { - margin: 0; - padding: 0; -} - -/* Based on http://phrogz.net/CSS/columns3.html */ -div.toc { - float: left; - margin: 0; - padding: 0; - padding-top: 0.5em; - border: 0; - width: 16em; - - background-color: #f9f9f9; - margin-right:1em; -} - -div.content { - margin: 0; - padding: 0; - - /* won't match if font is smaller in toc */ - border-left: 16em solid #f9f9f9; - padding-left: 1em; -} - -div.content:after { - content:' '; - clear:both; - display:block; - height:0; - overflow:hidden -} - -div.footer { - clear:left; - padding: 0.5em; - /* background-color: #f9f9f9; - border: 1px solid #aaaaaa; */ - font-size: 80%; - margin: 0; -} - -div.toc ul { - list-style: none; - padding: 0; - margin: 0; -} - -div.toc li ul a, li ul span.currentlink -{ - font-weight: normal; - font-size: 90%; - padding-left: 2em; -} - -div.toc a, span.currentlink{ - display:block; - text-decoration: none; - padding-left: 0.5em; - color: #0000aa; -} - -span.currentlink { - text-decoration: none; - background-color: #aaaaf9; -} - -div.toc a:visited { - color: #0000aa; -} - -div.toc a:hover { - background-color: #f9f9aa; -} - -.programlisting, .screen { - margin: 0; - border: 1px solid #aaaaaa; - background-color: #f9f9f9; - padding: 0.17em; - margin: 1em; - margin-right: 3em; -} - -.parameter { - font-style: italic; -} - -h1, h2 { - padding-top: 0.5em; - padding-bottom: 0.17em; - margin: 0; - font-weight: normal; - color: black; - border-bottom: 1px solid #aaaaaa; -} - -h1 { - font-size: 188%; -} - -div.chapter h2 { - font-size: 188%; -} - -div.section h2 { - font-size: 150%; -} + + + + + + + + + +Public Git Hosting - gitmagic.git/blob - book.css + + + + + + + + + + + + +
+ +
+ +
+Merge branch 'master' of https://github.com/matrig/gitmagic +
+
[gitmagic.git] / book.css
+
+
1 body {
+
2     font-size: 90%;
+
3     font-family: verdana, arial, sans-serif;
+
4 }
+ +
6 tt, code, pre, .type {
+
7     font-family: andale mono, courier new, courier, monospace;
+
8     font-size: 90%;
+
9 }
+ +
11 pre {
+
12     color: #0000aa;
+
13 }
+ +
15 ul li p {
+
16     margin: 0;
+
17     padding: 0;
+
18 }
+ +
20 /* Based on http://phrogz.net/CSS/columns3.html */
+
21 div.toc {
+
22     float: left;
+
23     margin: 0;
+
24     padding: 0;
+
25     padding-top: 0.5em;
+
26     border: 0;
+
27     width: 16em;
+ +
29     background-color: #f9f9f9;
+
30     margin-right:1em;
+
31 }
+ +
33 div.content {
+
34     margin: 0;
+
35     padding: 0;
+ +
37     /* won't match if font is smaller in toc */
+
38     border-left: 16em solid #f9f9f9;
+
39     padding-left: 1em;
+
40 }
+ +
42 div.content:after {
+
43     content:' ';
+
44     clear:both;
+
45     display:block;
+
46     height:0;
+
47     overflow:hidden
+
48 }
+ +
50 div.footer {
+
51     clear:left;
+
52     padding: 0.5em;
+
53     /* background-color: #f9f9f9;
+
54     border: 1px solid #aaaaaa; */
+
55     font-size: 80%;
+
56     margin: 0;
+
57 }
+ +
59 div.toc ul {
+
60     list-style: none;
+
61     padding: 0;
+
62     margin: 0;
+
63 }
+ +
65 div.toc li ul a, li ul span.currentlink
+
66 {
+
67     font-weight: normal;
+
68     font-size: 90%;
+
69     padding-left: 2em;
+
70 }
+ +
72 div.toc a, span.currentlink{
+
73     display:block;
+
74     text-decoration: none;
+
75     padding-left: 0.5em;
+
76     color: #0000aa;
+
77 }
+ +
79 span.currentlink {
+
80     text-decoration: none;
+
81     background-color: #aaaaf9;
+
82 }
+ +
84 div.toc a:visited {
+
85     color: #0000aa;
+
86 }
+ +
88 div.toc a:hover {
+
89     background-color: #f9f9aa;
+
90 }
+ +
92 .programlisting, .screen {
+
93     margin: 0;
+
94     border: 1px solid #aaaaaa;
+
95     background-color: #f9f9f9;
+
96     padding: 0.17em;
+
97     margin: 1em;
+
98     margin-right: 3em;
+
99 }
+ +
101 .parameter {
+
102     font-style: italic;
+ + +
105 h1, h2 {
+
106     padding-top: 0.5em;
+
107     padding-bottom: 0.17em;
+
108     margin: 0;
+
109     font-weight: normal;
+
110     color: black;
+
111     border-bottom: 1px solid #aaaaaa;
+ + +
114 h1 {
+
115     font-size: 188%;
+ + +
118 div.chapter h2 {
+
119     font-size: 188%;
+ + +
122 div.section h2 {
+
123     font-size: 150%;
+ +
+ + \ No newline at end of file diff --git a/de/Makefile b/de/Makefile deleted file mode 120000 index 0d2fefa..0000000 --- a/de/Makefile +++ /dev/null @@ -1 +0,0 @@ -../po4gitmagic/Makefile \ No newline at end of file diff --git a/de/Makefile b/de/Makefile new file mode 100644 index 0000000..0d2fefa --- /dev/null +++ b/de/Makefile @@ -0,0 +1 @@ +../po4gitmagic/Makefile \ No newline at end of file diff --git a/pl/basic.txt b/pl/basic.txt dissimilarity index 92% index 4b01142..57a2ce8 100644 --- a/pl/basic.txt +++ b/pl/basic.txt @@ -1,208 +1,196 @@ -== Basic Tricks == - -Rather than diving into a sea of Git commands, use these elementary examples to -get your feet wet. Despite their simplicity, each of them are useful. -Indeed, in my first months with Git I never ventured beyond the material in this chapter. - -=== Saving State === - -About to attempt something drastic? Before you do, take a snapshot of all files -in the current directory with: - - $ git init - $ git add . - $ git commit -m "My first backup" - -Now if your new edits go awry, restore the pristine version: - - $ git reset --hard - -To save the state again: - - $ git commit -a -m "Another backup" - -=== Add, Delete, Rename === - -The above only keeps track of the files that were present when you first ran *git add*. If you add new files or subdirectories, you'll have to tell Git: - - $ git add readme.txt Documentation - -Similarly, if you want Git to forget about certain files: - - $ git rm kludge.h obsolete.c - $ git rm -r incriminating/evidence/ - -Git deletes these files for you if you haven't already. - -Renaming a file is the same as removing the old name and adding the new name. There's also the shortcut *git mv* which has the same syntax as the *mv* command. For example: - - $ git mv bug.c feature.c - -=== Advanced Undo/Redo === - -Sometimes you just want to go back and forget about every change past a certain point because they're all wrong. Then: - - $ git log - -shows you a list of recent commits, and their SHA1 hashes: - ----------------------------------- -commit 766f9881690d240ba334153047649b8b8f11c664 -Author: Bob -Date: Tue Mar 14 01:59:26 2000 -0800 - - Replace printf() with write(). - -commit 82f5ea346a2e651544956a8653c0f58dc151275c -Author: Alice -Date: Thu Jan 1 00:00:00 1970 +0000 - - Initial commit. ----------------------------------- - -The first few characters of the hash are enough to specify the commit; -alternatively, copy and paste the entire hash. Type: - - $ git reset --hard 766f - -to restore the state to a given commit and erase all newer commits from the record permanently. - -Other times you want to hop to an old state briefly. In this case, type: - - $ git checkout 82f5 - -This takes you back in time, while preserving newer commits. However, like time travel in a science-fiction movie, if you now edit and commit, you will be in an alternate reality, because your actions are different to what they were the first time around. - -This alternate reality is called a 'branch', and <>. For now, just remember that - - $ git checkout master - -will take you back to the present. Also, to stop Git complaining, always -commit or reset your changes before running checkout. - -To take the computer game analogy again: - -- *`git reset --hard`*: load an old save and delete all saved games newer than the one just loaded. - -- *`git checkout`*: load an old game, but if you play on, the game state will deviate from the newer saves you made the first time around. Any saved games you make now will end up in a separate branch representing the alternate reality you have entered. <>. - -You can choose only to restore particular files and subdirectories by appending them after the command: - - $ git checkout 82f5 some.file another.file - -Take care, as this form of *checkout* can silently overwrite files. To -prevent accidents, commit before running any checkout command, especially when -first learning Git. In general, whenever you feel unsure about any operation, Git command or not, first run *git commit -a*. - -Don't like cutting and pasting hashes? Then use: - - $ git checkout :/"My first b" - -to jump to the commit that starts with a given message. -You can also ask for the 5th-last saved state: - - $ git checkout master~5 - -=== Reverting === - -In a court of law, events can be stricken from the record. Likewise, you can pick specific commits to undo. - - $ git commit -a - $ git revert 1b6d - -will undo just the commit with the given hash. The revert is recorded as a new -commit, which you can confirm by running *git log*. - -=== Changelog Generation === - -Some projects require a http://en.wikipedia.org/wiki/Changelog[changelog]. -Generate one by typing: - - $ git log > ChangeLog - -=== Downloading Files === - -Get a copy of a project managed with Git by typing: - - $ git clone git://server/path/to/files - -For example, to get all the files I used to create this site: - - $ git clone git://git.or.cz/gitmagic.git - -We'll have much to say about the *clone* command soon. - -=== The Bleeding Edge === - -If you've already downloaded a copy of a project using *git clone*, you can upgrade to the latest version with: - - $ git pull - -=== Instant Publishing === - -Suppose you've written a script you'd like to share with others. You could just tell them to download from your computer, but if they do so while you're improving the script or making experimental changes, they could wind up in trouble. Of course, this is why release cycles exist. Developers may work on a project frequently, but they only make the code available when they feel it is presentable. - -To do this with Git, in the directory where your script resides: - - $ git init - $ git add . - $ git commit -m "First release" - -Then tell your users to run: - - $ git clone your.computer:/path/to/script - -to download your script. This assumes they have ssh access. If not, run *git daemon* and tell your users to instead run: - - $ git clone git://your.computer/path/to/script - -From now on, every time your script is ready for release, execute: - - $ git commit -a -m "Next release" - -and your users can upgrade their version by changing to the directory containing your script and typing: - - $ git pull - -Your users will never end up with a version of your script you don't want them to see. - -=== What Have I Done? === - -Find out what changes you've made since the last commit with: - - $ git diff - -Or since yesterday: - - $ git diff "@{yesterday}" - -Or between a particular version and 2 versions ago: - - $ git diff 1b6d "master~2" - -In each case the output is a patch that can be applied with *git apply*. -Try also: - - $ git whatchanged --since="2 weeks ago" - -Often I'll browse history with http://sourceforge.net/projects/qgit[qgit] instead, due to its slick photogenic interface, or http://jonas.nitro.dk/tig/[tig], a text-mode interface that works well over slow connections. Alternatively, install a web server, run *git instaweb* and fire up any web browser. - -=== Exercise === - -Let A, B, C, D be four successive commits where B is the same as A except some files have been removed. We want to add the files back at D. How can we do this? - -There are at least three solutions. Assuming we are at D: - - 1. The difference between A and B are the removed files. We can create a patch representing this difference and apply it: - - $ git diff B A | git apply - - 2. Since we saved the files back at A, we can retrieve them: - - $ git checkout A foo.c bar.h - - 3. We can view going from A to B as a change we want to undo: - - $ git revert B - -Which choice is best? Whichever you prefer most. It is easy to get what you want with Git, and often there are many ways to get it. +== Pierwsze kroki == + +Zanim utoniemy w morzu poleceń Gita, przyjrzyjmy się najpierw kilku prostym poleceniom. Pomimo ich prostoty, wszystkie jednak są ważne i pożyteczne. W rzeczywistości, podczas pierwszych miesięcy pracy z Git nie wychodziłem poza zakres opisany w tym rozdziale + +=== Zabezpieczenie obecnego stanu === + +Zamierzasz przeprowadzić jakieś drastyczne zmiany? Zanim to zrobisz, zabezpiecz najpierw dane w aktualnym katalogu. + + $ git init + $ git add . + $ git commit -m "Mój pierwszy commit" + +Teraz, jeśli cokolwiek stałoby się z twymi plikami podczas edycji, możesz przywrócić pierwotną wersję: + + $ git reset --hard + +Aby zapisać nową wersję: + + $ git commit -a -m "Mój następny commit" + +=== Dodanie, kasowanie i zmiana nazwy === + +Powyższa komenda zatrzyma jedynie pliki, które już istniały podczas gdy po raz pierwszy wykonałaś polecenie *git add*. Jeśli w międzyczasie dodałaś jakieś nowe pliki, Git musi zostać o tym poinformowany: + + $ git add readme.txt Dokumentacja + +To samo, gdy zechcesz by Git zapomniał o wybranych plikach: + + $ git rm ramsch.h archaiczne.c + $ git rm -r obciążający/materiał/ + +Jeśli sam tego jeszcze nie zrobiłaś, to Git usunie pliki za ciebie. + +Zmiana nazwy pliku, to jak jego skasowanie i ponowne utworzenie z nową nazwą. Git wykorzystuje do tego skrót *git mv*, który posiada tą samą składnię co polecenie *mv*. Na przykład: + + $ git mv bug.c feature.c + +=== Zaawansowane anulowanie/przywracanie === + +Czasami zechcesz po prostu cofnąć się w czasie, zapominając o wszystkich wprowadzonych od tego punktu zmianach. Wtedy: + + $ git log + +pokaże ci listę dotychczasowych 'commits' i ich sum kontrolnych SHA1: + +---------------------------------- +commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob +Date: Tue Mar 14 01:59:26 2000 -0800 + + Zamień printf() na write(). + +commit 82f5ea346a2e651544956a8653c0f58dc151275c +Author: Alicja +Date: Thu Jan 1 00:00:00 1970 +0000 + + Initial commit. +---------------------------------- + +Kilka początkowych znaków sumy kontrolnej SHA1 wystarcza by jednoznacznie zidentyfikować 'commit', alternatywnie możesz skopiować i wkleić cały hash. Wpisując: + + $ git reset --hard 766f + +przywrócisz stan do wersji żądanego 'commit', a wszystkie późniejsze zmiany zostaną bezpowrotnie skasowane. + +Innym razem chcesz tylko na moment przejść do jednej z poprzednich wersji. W tym wypadku użyj komendy: + + $ git checkout 82f5 + +Tym poleceniem wrócisz się w czasie zachowując nowsze zmiany. Ale, tak samo jak w podróżach w czasie z filmów science-fiction - jeśli teraz dokonasz zmian i zapamiętasz je poleceniem 'commit', zostaniesz przeniesiona do alternatywnej rzeczywistości, ponieważ twoje zmiany różnią się od już dokonanych w późniejszych punktach czasu. + +Tą alternatywną rzeczywistość nazywamy 'branch', a <>. Na razie, zapamiętaj tylko, że: + + $ git checkout master + +sprowadzi cię znów do teraźniejszości. Również, aby uprzedzić narzekanie Gita, powinnaś przed każdym 'checkout' wykonać 'commit' lub 'reset'. + +Korzystając ponownie z analogii do gier komputerowych: + +- *`git reset --hard`*: załaduj jakiś starszy stan gry i skasuj wszystkie nowsze niż właśnie ładowany. + +- *`git checkout`*: Załaduj stary stan, grając dalej, twój stan będzie się różnił od nowszych zapamiętanych. Każdy stan, który zapamiętasz od teraz, powstanie jako osobna gałęź ('branch'), reprezentującym alternatywną rzeczywistość. <> + +Jeśli chcesz, możesz przywrócić jedynie wybrane pliki lub katalogi poprzez dodanie ich nazw do polecenia: + + $ git checkout 82f5 jeden.plik inny.plik + +Bądź ostrożna, ten sposób użycia komendy *checkout* może bez uprzedzenia skasować pliki. Aby zabezpieczyć się przed takimi wypadkami powinnaś zawsze zrobić 'commit' zanim wpiszesz 'checkout', szczególnie w okresie poznawania Gita. Jeśli czujesz się niepewnie przed wykonaniem jakiejś operacji Gita, generalną zasadą powinno stać się dla ciebie uprzednie wykonanie *git commit -a*. + +Nie lubisz kopiować i wklejać hashów SHA1? Możesz w tym wypadku skorzystać z: + + $ git checkout :/"Mój pierwszy c" + +by przenieś się do 'commit', którego opis rozpoczyna się jak zawarta wiadomość. Możesz również cofnąć się do piątego z ostatnio zapamiętanych 'commit': + + $ git checkout master~5 + +=== Przywracanie === + +W sali sądowej pewne zdarzenia mogą zostać wykreślone z akt. Podobnie możesz zaznaczyć pewne 'commits' do wykreślenia. + + $ git commit -a + $ git revert 1b6d + +To polecenie wymaże 'commit' o wybranym hashu. Ten rewers zostanie zapamiętany jednak jako nowy 'commit', co można sprawdzić poleceniem *git log*. + +=== Generowanie listy zmian === + +Niektóre projekty wymagają http://en.wikipedia.org/wiki/Changelog[pliku changelog]. Wygenerujesz go poleceniem: + + $ git log > changelog + +=== Ładowanie plików === + +Kopię projektu zarządzanego za pomocą Gita uzyskasz poleceniem: + + $ git clone git://ścieżka/do/projektu + +By na przykład zładować wszystkie dane, których użyłem do stworzenia tej strony skorzystaj z: + + $ git clone git://git.or.cz/gitmagic.git + +Do polecenia 'clone' wrócimy niebawem. + +=== Najnowszy stan === + +Jeśli posiadasz już kopię projektu wykonaną za pomocą *git clone*, możesz ją zaktualizować poleceniem: + + $ git pull + +=== Szybka publikacja === + +Przypuśćmy, że napisałaś skrypt i chcesz go udostępnić innym. Mogłabyś poprosić ich, by zładowali go bezpośrednio z twojego komputera. Jeśli jednak zrobią to podczas gdy ty jeszcze wprowadzasz poprawki lub eksperymentujesz ze zmianami, mogłabyś przysporzyć im nieprzyjemności. Z tego powodu istnieje coś takiego jak cykl wydawniczy. Programiści regularnie pracują nad projektem, upubliczniają kod jednak dopiero, jeśli uznają, że nadaje się już do pokazania. + +Aby wykonać to za pomocą GIT, wejdź do katalogu w którym znajduje się twój skrypt: + + $ git init + $ git add . + $ git commit -m "Pierwsze wydanie" + +Następnie poproś twych użytkowników o wykonanie: + + $ git clone twój.komputer:/ścieżka/do/skryptu + +by zładować twój skrypt. Zakładamy tu posiadanie przez nich klucza SSH do twojego komputera. Jeśli go nie mają, uruchom *git daemon* i podaj im następujący link: + + $ git clone git://twój.komputer/ścieżka/do/skryptu + +Od teraz, zawsze gdy uznasz, że wersja nadaje się do opublikowania, wykonaj polecenie: + + $ git commit -a -m "Następna wersja" + +a twoi użytkownicy, po wejściu do katalogu zawierającego twój skrypt, będą go mogli zaktualizować poprzez: + + $ git pull + +Twoi użytkownicy nigdy nie wejdą w posiadanie wersji, których nie chcesz im udostępniać. + +=== A co robiłem ostatnio? === + +Jeśli chcesz zobaczyć zmiany, które wprowadziłaś od ostatniego 'commit', wpisz: + + $ git diff + +Albo tylko zmiany od wczoraj: + + $ git diff "@{yesterday}" + +Albo miedzy określoną wersją i dwoma poprzedzającymi: + + $ git diff 1b6d "master~2" + +Za każdym razem uzyskane informacje są równocześnie patchem, który poprzez *git apply* może być zastosowany. Spróbuj również: + + $ git whatchanged --since="2 weeks ago" + +Jeśli chcę sprawdzić listę zmian jakiegoś repozytorium, często korzystam z http://sourceforge.net/projects/qgit[qgit], ze względu na jego fotogeniczny interfejs, albo z http://jonas.nitro.dk/tig/[tig], tekstowy interfejs, działający zadowalająco gdy mamy do czynienia z wolnym łączem internetowym. Alternatywnie, zainstaluj serwer HTTP, uruchom *git instaweb* i odpal dowolną przeglądarkę internetową. + +=== Ćwiczenie === + +Niech A, B, C i D będą 4 następującymi po sobie 'commits', gdzie B różni się od A, jedynie tym, iż usunięto kilka plików. Chcemy teraz te usunięte pliki zrekonstruować do D. Jak to można zrobić? + +Istnieją przynajmniej 3 rozwiązania. Załóżmy że znajdujemy się obecnie w D: + +1. Różnica pomiędzy A i B, to skasowane pliki. Możemy utworzyć patch, który pokaże te różnice i następnie zastosować go: + + $ git diff B A | git apply + +2. Ponieważ pliki zostały już raz zapamiętane w A, możemy je przywrócić: + + $ git checkout A foo.c bar.h + +3. Możemy też widzieć przejście z A na B jako zmianę, którą można zrewertować: + + $ git revert B + +A które z tych rozwiązań jest najlepsze? To, które najbardziej tobie odpowiada. Korzystając z Git łatwo osiągnąć cel, czasami prowadzi do niego wiele dróg. diff --git a/pl/branch.txt b/pl/branch.txt dissimilarity index 97% index 84c27d0..84c239c 100644 --- a/pl/branch.txt +++ b/pl/branch.txt @@ -1,245 +1,190 @@ -== Branch Wizardry == - -Instant branching and merging are the most lethal of Git's killer features. - -*Problem*: External factors inevitably necessitate context switching. A severe -bug manifests in the released version without warning. The deadline for a -certain feature is moved closer. A developer whose help you need for a key section of the project is about to leave. In all cases, you must abruptly drop what you are doing and focus on a completely different task. - -Interrupting your train of thought can be detrimental to your productivity, and the more cumbersome it is to switch contexts, the greater the loss. With centralized version control we must download a fresh working copy from the central server. Distributed systems fare better, as we can clone the desired version locally. - -But cloning still entails copying the whole working directory as well as the entire history up to the given point. Even though Git reduces the cost of this with file sharing and hard links, the project files themselves must be recreated in their entirety in the new working directory. - -*Solution*: Git has a better tool for these situations that is much faster and more space-efficient than cloning: *git branch*. - -With this magic word, the files in your directory suddenly shapeshift from one version to another. This transformation can do more than merely go back or forward in history. Your files can morph from the last release to the experimental version to the current development version to your friend's version and so on. - -=== The Boss Key === - -Ever played one of those games where at the push of a button (``the boss key''), the screen would instantly display a spreadsheet or something? So if the boss walked in the office while you were playing the game you could quickly hide it away? - -In some directory: - - $ echo "I'm smarter than my boss" > myfile.txt - $ git init - $ git add . - $ git commit -m "Initial commit" - -We have created a Git repository that tracks one text file containing a certain message. Now type: - - $ git checkout -b boss # nothing seems to change after this - $ echo "My boss is smarter than me" > myfile.txt - $ git commit -a -m "Another commit" - -It looks like we've just overwritten our file and committed it. But it's an illusion. Type: - - $ git checkout master # switch to original version of the file - -and hey presto! The text file is restored. And if the boss decides to snoop around this directory, type: - - $ git checkout boss # switch to version suitable for boss' eyes - -You can switch between the two versions of the file as much as you like, and commit to each independently. - -=== Dirty Work === - -[[branch]] -Say you're working on some feature, and for some reason, you need to go back three versions and temporarily put in a few print statements to see how something works. Then: - - $ git commit -a - $ git checkout HEAD~3 - -Now you can add ugly temporary code all over the place. You can even commit these changes. When you're done, - - $ git checkout master - -to return to your original work. Observe that any uncommitted changes are carried over. - -What if you wanted to save the temporary changes after all? Easy: - - $ git checkout -b dirty - -and commit before switching back to the master branch. Whenever you want to return to the dirty changes, simply type: - - $ git checkout dirty - -We touched upon this command in an earlier chapter, when discussing loading old states. At last we can tell the whole story: the files change to the requested state, but we must leave the master branch. Any commits made from now on take your files down a different road, which can be named later. - -In other words, after checking out an old state, Git automatically puts you in a new, unnamed branch, which can be named and saved with *git checkout -b*. - -=== Quick Fixes === - -You're in the middle of something when you are told to drop everything and fix a newly discovered bug in commit `1b6d...`: - - $ git commit -a - $ git checkout -b fixes 1b6d - -Then once you've fixed the bug: - - $ git commit -a -m "Bug fixed" - $ git checkout master - -and resume work on your original task. You can even 'merge' in the freshly -baked bugfix: - - $ git merge fixes - -=== Merging === - -With some version control systems, creating branches is easy but merging them -back together is tough. With Git, merging is so trivial that you might be -unaware of it happening. - -We actually encountered merging long ago. The *pull* command in fact 'fetches' -commits and then merges them into your current branch. If you have no local -changes, then the merge is a 'fast forward', a degenerate case akin to fetching -the latest version in a centralized version control system. But if you do have -local changes, Git will automatically merge, and report any conflicts. - -Ordinarily, a commit has exactly one 'parent commit', namely, the previous -commit. Merging branches together produces a commit with at least two parents. -This begs the question: what commit does `HEAD~10` really refer to? A commit -could have multiple parents, so which one do we follow? - -It turns out this notation chooses the first parent every time. This is -desirable because the current branch becomes the first parent during a merge; -frequently you're only concerned with the changes you made in the current -branch, as opposed to changes merged in from other branches. - -You can refer to a specific parent with a caret. For example, to show -the logs from the second parent: - - $ git log HEAD^2 - -You may omit the number for the first parent. For example, to show the -differences with the first parent: - - $ git diff HEAD^ - -You can combine this notation with other types. For example: - - $ git checkout 1b6d^^2~10 -b ancient - -starts a new branch ``ancient'' representing the state 10 commits back from the -second parent of the first parent of the commit starting with 1b6d. - -=== Uninterrupted Workflow === - -Often in hardware projects, the second step of a plan must await the completion of the first step. A car undergoing repairs might sit idly in a garage until a particular part arrives from the factory. A prototype might wait for a chip to be fabricated before construction can continue. - -Software projects can be similar. The second part of a new feature may have to -wait until the first part has been released and tested. Some projects require -your code to be reviewed before accepting it, so you might wait until the first -part is approved before starting the second part. - -Thanks to painless branching and merging, we can bend the rules and work on -Part II before Part I is officially ready. Suppose you have committed Part I -and sent it for review. Let's say you're in the `master` branch. Then branch -off: - - $ git checkout -b part2 - -Next, work on Part II, committing your changes along the way. To err is human, -and often you'll want to go back and fix something in Part I. -If you're lucky, or very good, you can skip these lines. - - $ git checkout master # Go back to Part I. - $ fix_problem - $ git commit -a # Commit the fixes. - $ git checkout part2 # Go back to Part II. - $ git merge master # Merge in those fixes. - -Eventually, Part I is approved: - - $ git checkout master # Go back to Part I. - $ submit files # Release to the world! - $ git merge part2 # Merge in Part II. - $ git branch -d part2 # Delete "part2" branch. - -Now you're in the `master` branch again, with Part II in the working directory. - -It's easy to extend this trick for any number of parts. It's also easy to -branch off retroactively: suppose you belatedly realize you should have created -a branch 7 commits ago. Then type: - - $ git branch -m master part2 # Rename "master" branch to "part2". - $ git branch master HEAD~7 # Create new "master", 7 commits upstream. - -The `master` branch now contains just Part I, and the `part2` branch contains -the rest. We are in the latter branch; we created `master` without switching to -it, because we want to continue work on `part2`. This is unusual. Until now, -we've been switching to branches immediately after creation, as in: - - $ git checkout HEAD~7 -b master # Create a branch, and switch to it. - -=== Reorganizing a Medley === - -Perhaps you like to work on all aspects of a project in the same branch. You want to keep works-in-progress to yourself and want others to see your commits only when they have been neatly organized. Start a couple of branches: - - $ git branch sanitized # Create a branch for sanitized commits. - $ git checkout -b medley # Create and switch to a branch to work in. - -Next, work on anything: fix bugs, add features, add temporary code, and so forth, committing often along the way. Then: - - $ git checkout sanitized - $ git cherry-pick medley^^ - -applies the grandparent of the head commit of the ``medley'' branch to the ``sanitized'' branch. With appropriate cherry-picks you can construct a branch that contains only permanent code, and has related commits grouped together. - -=== Managing Branches === - -List all branches by typing: - - $ git branch - -By default, you start in a branch named ``master''. Some advocate leaving the -``master'' branch untouched and creating new branches for your own edits. - -The *-d* and *-m* options allow you to delete and move (rename) branches. -See *git help branch*. - -The ``master'' branch is a useful custom. Others may assume that your -repository has a branch with this name, and that it contains the official -version of your project. Although you can rename or obliterate the ``master'' -branch, you might as well respect this convention. - -=== Temporary Branches === - -After a while you may realize you are creating short-lived branches -frequently for similar reasons: every other branch merely serves to -save the current state so you can briefly hop back to an older state to -fix a high-priority bug or something. - -It's analogous to changing the TV channel temporarily to see what else is on. -But instead of pushing a couple of buttons, you have to create, check out, -merge, and delete temporary branches. Luckily, Git has a shortcut that is as -convenient as a TV remote control: - - $ git stash - -This saves the current state in a temporary location (a 'stash') and -restores the previous state. Your working directory appears exactly as it was -before you started editing, and you can fix bugs, pull in upstream changes, and -so on. When you want to go back to the stashed state, type: - - $ git stash apply # You may need to resolve some conflicts. - -You can have multiple stashes, and manipulate them in various ways. See -*git help stash*. As you may have guessed, Git maintains branches behind the scenes to perform this magic trick. - -=== Work How You Want === - -You might wonder if branches are worth the bother. After all, clones are almost -as fast, and you can switch between them with *cd* instead of esoteric Git -commands. - -Consider web browsers. Why support multiple tabs as well as multiple windows? -Because allowing both accommodates a wide variety of styles. Some users like to -keep only one browser window open, and use tabs for multiple webpages. Others -might insist on the other extreme: multiple windows with no tabs anywhere. -Others still prefer something in between. - -Branching is like tabs for your working directory, and cloning is like opening -a new browser window. These operations are fast and local, so why not -experiment to find the combination that best suits you? Git lets you work -exactly how you want. +== Magia 'branch' == + +Szybkie, natychmiastowe działanie poleceń 'branch' i 'merge', to jedne z najbardziej zabójczych właściwości Gita. + +*Problem*: Zewnętrzne faktory narzucają konieczność zmiany kontekstu. Poważne błędy w opublikowanej wersji ujawniły się bez ostrzeżenia. Skrócono termin opublikowania pewnej właściwości. Autor, którego pomocy potrzebujesz w jednej z kluczowych sekcji postanawia opóścić projekt. We wszystkich tych przypadkach musisz natychmiastowo zaprzestać bieżących prac i skoncentrować się nad zupełnie innymi zadaniami. + +Przerwanie toku myślenia nie jest dobre dla produktywności, a czym większa różnica w kontekście, tym większe straty. Używając centralnych systemów kontroli wersji musielibyśmy najpierw zładować świeżą kopię roboczą z serwera. W systemach rozproszonych wygląda to dużo lepiej, ponieważ możemy żądaną wersje sklonować lokalnie + +Jednak klonowanie również niesie za sobą kopiowanie całego katalogu roboczego jak i całej historii projektu aż do żądanego punktu. Nawet jeśli Git redukuje wielkość przez podział danych i użycie twardych dowiązań, wszystkie pliki projektu muszą zostać odtworzone w nowym katalogu roboczym. + +*Rozwiązanie*: Git posiada lepsze narzędzia dla takich sytuacji, jest ono wiele szybsze i zajmujące mniej miejsca na dysku jak klonowanie: *git branch*. + +Tym magicznym słowem zmienisz dane w swoim katalogu roboczym z jednej wersji w inną. Ta przemiana potrafi dużo więcej jak tylko poruszać się w historii projektu. Twoje pliki mogą przekształcić się z aktualnej wersji do wersji eksperymentalnej, do wersji testowej, do wersji twojego kolegi i tak dalej. + +=== Przycisk 'szef' === + +Być może grałaś już kiedyś w grę, która posiadała magiczny (``przycisk szef''), po naciśnięciu którego twój monitor natychmiast pokazywał jakieś arkusze kalkulacyjne, czy coś w tym roduaju? Celem przycisku było szybkie ukrycie gierki na wypadek pojawienia się szefa w twoim biurze. + +W jakimś katalogu: + + $ echo "Jestem mądrzejsza od szefa" > mój_plik.txt + $ git init + $ git add . + $ git commit -m "Pierwszy commit" + +Utworzyliśmy repozytorium Gita, które zawiera plik o powyższej zawartości. Następnie wpisujemy: + + $ git checkout -b szef # wydaje się, jakby nic się nie stało + $ echo "Mój szef jest ode mnie mądrzejszy" > mój_plik.txt + $ git commit -a -m "Druga wersja" + +Wygląda jakbyśmy zmienili zawartość pliku i wykonali 'commit'. Ale to tylko iluzja. Wpisz: + + $ git checkout master # przejdź do oryginalnej wersji + +i hokus-pokus! Poprzedni plik jest przywrócony do stanu pierwotnego. Gdyby jednak szef zdecydował się grzebać w twoim katalogu, wpisz: + + $ git checkout szef # przejdź do wersji, która nadaje się do obejrzenia przez szefa + +Możesz zmieniać pomiędzy tymi wersjami pliku tak często jak zechcesz, każdą z tych wersji pliku możesz też niezależnie edytować. + +=== Brudna robota === + +[[branch]] +Załóżmy, że pracujesz nad jakąś funkcją i musisz z jakiegokolwiek powodu wrócić o 3 wersje wstecz w celu wprowadzenia kilku poleceń print, aby sprawdzić jej działanie. Wtedy: + + $ git commit -a + $ git checkout HEAD~3 + +Teraz możesz na dziko wprowadzać tymczasowy kod. Możesz te zmiany nawet dodać do'commit'. Po skończeniu, + + $ git checkout master + +wróci cię do poprzedniej pracy. Zauważ, że wszystkie zmiany, które nie zostały zatwierdzone przez 'commit', zostały przejęte. + +A co jeśli chciałaś zapamiętać wprowadzone zmiany? Proste: + + $ git checkout -b brudy + +i tylko jeszcze wykonaj 'commit' zanim wrócisz do 'master branch'. Jeśli tylko chcesz wrócić do twojej brudnej roboty, wpisz po prostu + + $ git checkout brudy + +Spotkaliśmy się z tym poleceniem już we wcześniejszym rozdziale, gdy poruszaliśmy temat ładowania starych wersji. Teraz możemy opowiedzieć cala prawdę: pliki zmieniają się do żądanej wersji, jednak musimy opuścić 'master branch'. Każdy 'commit' od teraz prowadzi twoje dane inną drogą, której możemy również nadać nazwę. + +Innymi słowami, po przywołaniu ('checkout') starszego stanu Git automatycznie przenosi cię do nowego, nienazwanego 'branch', który poleceniem *git checkout -b* otrzyma nazwę i zostanie zapamiętany. + +=== Szybka korekta błędów === + +Będąc w środku jakiejś pracy, otrzymujesz polecenie zajęcia się nowo znalezionym błędem w 'commit' `1b6d...`: + + $ git commit -a + $ git checkout -b fixes 1b6d + +Po skorygowaniu błędu: + + $ git commit -a -m "Błąd usunięty" + $ git checkout master + +i kontynuujesz przerwaną pracę. Możesz nawet ostatnio świeżo upieczoną poprawkę przejąć do aktualnej wersji: + + $ git merge fixes + +=== Merge === + +Za pomocą niektórych systemów kontroli wersji utworzenie nowego 'branch' może i jest proste, jednak późniejsze połączenie ('merge') skomplikowane. Z Gitem 'merge' jest tak trywialne, że możesz czasem nawet nie zostać powiadomiony o jego wykonaniu. + +W gruncie rzeczy spotkaliśmy się już dużo wcześniej z funkcją 'merge'. Polecenie *git pull* ściąga inne wersje i łączy ('merge') z twoim aktualnym 'branch'. Jeśli nie wprowadziłaś żadnych lokalnych zmian, to 'merge' jest szybkim przejściem do przodu, jest to przypadek podobny do zładowania ostatniej wersji przy centralnych systemach kontroli wersji. Jeśli jednak wprowadziłaś zmiany, Git automatycznie wykona 'merge' i powiadomi cię o ewentualnych konfliktach. + +Zwyczajnie każdy 'commit' posiada matczyny 'commit', a mianowicie poprzedzający go 'commit'. Zespolenie kilku 'branch' wytwarza 'commit' z minimum 2 matczynymi 'commit'. To nasuwa pytanie, który właściwie 'commit' wskazuje na `HEAD~10`? Każdy 'commit' może posiadać więcej rodziców, za którym właściwie podążamy? + +Wychodzi na to, ze ta notacja zawsze wybiera pierwszego rodzica. To jest wskazane, ponieważ aktualny 'branch' staje się pierwszym rodzicem dla 'merge', częściej będziesz zainteresowany bardziej zmianami których dokonałaś w aktualnym 'branch', niż w innych. + +Możesz też wybranego rodzica wskazać używając symbol dzióbka. By na przykład pokazać logi drugiego rodzica. + + $ git log HEAD^2 + +Możesz pominąć numer pierwszego rodzica. By na przykład pokazać różnice z pierwszym rodzicem: + + $ git diff HEAD^ + +Możesz ta notacje kombinować także z innymi rodzajami. Na przykład: + + $ git checkout 1b6d^^2~10 -b archaiczne + +tworzy nowy 'branch' o nazwie 'archaiczne', reprezentujący stan 10 'commit' do tyłu drugiego rodzica dla pierwszego rodzica 'commit', którego hash rozpoczyna się na 1b6d. + +=== Praca bez przestojów === + +W procesie produkcji często drugi krok planu musi czekać na zakończenie pierwszego. Popsuty samochód stoi w garażu nieużywany do czasu dostarczenia części zamiennej. Prototyp musi czekać na wyprodukowanie jakiegoś chipa zanim będzie można podjąć dalszą konstrukcje. + +W projektach software może to wyglądać podobnie. Druga część jakiegoś feature musi czekać, aż pierwsza zostanie wydana i przetestowana. Niektóre projekty wymagają sprawdzenia twojego kodu zanim zostanie zaakceptowany, musisz wiec czekać z następną częścią aż pierwsza zostanie sprawdzona. + +Dzięki bezbolesnemu 'branch' i 'merge' możemy te reguły naciągnąć i pracować nad druga częścią jeszcze zanim pierwsza zostanie oficjalnie zatwierdzona. Przyjmijmy, że wykonałaś 'commit' pierwszej części i przekazałaś do sprawdzenia. Przyjmijmy też, że znajdujesz się w 'master branch'. Najpierw przejdź do 'branch' o nazwie 'część2': + +$ git checkout -b część2 + +Pracujesz w części 2 i regularnie wykonujesz 'commit'. Błądzenie jest ludzkie i może się zdarzyć, że zechcesz wrócić do części 1 i wprowadzić jakieś poprawki. Jeśli masz szczęście albo jesteś bardzo dobry, możesz ominąć następne linijki. + + $ git checkout master # przejdź do części 1 + $ fix_problem + $ git commit -a # zapisz rozwiązanie + $ git checkout część2 # przejdź do części 2 + $ git merge master # połącz zmiany + +Ewentualnie, część pierwsza zostaje dopuszczona: + + $ git checkout master # przejdź do części 1 + $ submit files # opublikuj twoja wersję + $ git merge część2 # Połącz z częścią 2 + $ git branch -d część2 # usuń branch część2 + +Znajdujesz się teraz z powrotem w 'master branch' posiadając 'część2' w katalogu roboczym. + +Dość łatwo zastosować ten sam trik na dowolną ilość części. Równie łatwo można utworzyć 'branch' wstecznie: przypuśćmy, właśnie spostrzegłaś, iż już właściwie jakieś 7 'commit' wcześniej powinnaś stworzyć 'branch'. Wpisz wtedy: + + $ git branch -m master część2 # Zmień nazwę "master" na "część2". + $ git branch master HEAD~7 # utwórz ponownie "master" 7 'commits' do tyłu. + +Teraz 'master branch' zawiera cześć 1 a branch `część2` zawiera całą resztę. Znajdujemy się teraz w tym ostatnim 'branch'; utworzyliśmy `master` bez wchodzenia do niego, gdyż zamierzamy dalszą pracę prowadzić w 'branch' `część2`. Nie jest to zbyt często stosowane. Do tej pory przechodziliśmy do nowego 'branch' zaraz po jego utworzeniu, tak jak w: + + $ git checkout HEAD~7 -b master # Utwórz branch i wejdź do niego. + +=== Reorganizacja składanki === + +Może lubisz odpracowywać wszystkie aspekty projektu w jednym 'branch'. Chcesz wszystkie bieżące zmiany zachować dla siebie, a wszyscy inni powinni zobaczyć twoje 'commit' po ich starannym zorganizowaniu. Wystartuj parę 'branch': + + $ git branch czyste # Utwórz branch dla oczyszczonych 'commits'. + $ git checkout -b zbieranina # utwórz 'branch' i przejdź do niego w celu dalszej pracy. + +Następnie wykonaj zamierzone prace: pousuwaj błędy, dodaj nowe funkcje, utwóż kod tymczasowy i tak dalej, regularnie wykonując 'commit'. Wtedy: + + $ git checkout czyste + $ git cherry-pick zbieranina^^ + +zastosuje najstarszy matczyny 'commit' z 'branch' ``zbieranina'' na 'branch' ``czyste''. Poprzez 'przebranie wisienek' możesz tak skonstruować 'branch', który posiada jedynie końcowy kod i zależne od niego pogrupowane 'commit'. + +=== Zarządzanie 'branch' === + +Listę wszystkich 'branch' otrzymasz poprzez: + + $ git branch + +Standardowo zaczynasz w 'branch' zwanym ``master''. Wielu opowiada się za pozostawieniem ``master'' branch w stanie dziewiczym i tworzeniu nowych dla twoich prac. + +Opcje *-d* und *-m* pozwalają na usuwanie i przesuwanie (zmianę nazwy) 'branch'. Zobacz: *git help branch*. + +Nazwa ``master'' jest bardzo użytecznym stworem. Inni mogą wychodzić z założenia, że twoje repozytorium takowy posiada i że zawiera on oficjalną wersję projektu. Nawet jeśli mogłabyś skasować lub zmienić nazwę na inną powinnaś respektować tę konwencję. + +=== Tymczasowe 'branch' === + +Po jakimś czasie zapewne zauważysz, że często tworzysz 'branch' o krótkiej żywotności, w większości z tego samego powodu: każdy nowy 'branch' służy jedynie do tego, by zabezpieczyć aktualny stan, aby móc wrócić do jednego z poprzednich punktów i poprawić jakieś priorytetowe błędy czy cokolwiek innego. + +Można to porównać do chwilowego przełączenia kanału telewizyjnego, by sprawdzić co dzieje się na innym. Lecz zamiast naciskać guziki pilota, korzystasz z poleceń 'create', 'checkout', 'merge' i 'delete'. Na szczęście Git posiada na te operacje skrót, który jest tak samo komfortowy jak pilot telewizora: + + $ git stash + +Polecenie to zabezpiecza aktualny stan w tymczasowym miejscu ('stash' = ukryj) i przywraca poprzedni stan. Twój katalog roboczy wygląda dokładnie tak, jak wyglądał zanim zacząłaś edycję. Teraz możesz poprawiać błędy, zładować zmiany z centralnego repozytorium ('pull') i tak dalej. Jeśli chcesz powrócić z powrotem do ukrytego ('stashed') stanu, wpisz: + + $ git stash apply # Prawdopodobnie będziesz musiał rozwiązać konflikty. + +Możesz posiadać więcej 'stash'-ów i traktować je w zupełnie inny sposób. Zobacz *git help stash*. Jak już prawdopodobnie się domyślasz, Git korzysta przy wykonywaniu tej magicznej sztuczki z funkcji 'branch' w tle. + +=== Pracuj jak chcesz === + +Może pytasz się, czy 'branch' są warte tego zachodu. Jakby nie było, polecenia 'clone' są prawie tak samo szybkie i możesz po prostu poleceniem *cd* zmieniać pomiędzy nimi, bez stosowania ezoterycznych poleceń Gita. + +Przyjrzyjmy się takiej przeglądarce internetowej. Dlaczego pozwala używać tabów tak samo jak i nowych okien? Ponieważ udostępnienie obu możliwości pozwala na stosowanie wielu stylów. Niektórzy użytkownicy preferują otwarcie jednego okna przeglądarki i korzystają z tabów dla wyświetlenia różnych stron. Inni upierają się przy stosowaniu pojedynczych okien dla każdej strony, zupełnie bez korzystania z tabów. Jeszcze inni znowu wolą coś pomiędzy. + +Stosowanie 'branch' to jak korzystanie z tabów dla twojego katalogu roboczego, a klonowanie porównać można do otwarcia wielu okien przeglądarki. Obie operacje są szybkie i lokalne, dlaczego nie poeksperymentować i nie znaleźć dla siebie najbardziej odpowiedniej kombinacji. Git pozwoli ci pracować dokładnie tak jak chcesz. diff --git a/pl/clone.txt b/pl/clone.txt dissimilarity index 94% index e168dae..c0c4b19 100644 --- a/pl/clone.txt +++ b/pl/clone.txt @@ -1,218 +1,194 @@ -== Cloning Around == - -In older version control systems, checkout is the standard operation to get files. You retrieve a bunch of files in a particular saved state. - -In Git and other distributed version control systems, cloning is the standard operation. To get files, you create a 'clone' of the entire repository. In other words, you practically mirror the central server. Anything the main repository can do, you can do. - -=== Sync Computers === - -I can tolerate making tarballs or using *rsync* for backups and basic syncing. But sometimes I edit on my laptop, other times on my desktop, and the two may not have talked to each other in between. - -Initialize a Git repository and commit your files on one machine. Then on the other: - - $ git clone other.computer:/path/to/files - -to create a second copy of the files and Git repository. From now on, - - $ git commit -a - $ git pull other.computer:/path/to/files HEAD - -will 'pull' in the state of the files on the other computer into the one you're working on. If you've recently made conflicting edits in the same file, Git will let you know and you should commit again after resolving them. - -=== Classic Source Control === - -Initialize a Git repository for your files: - - $ git init - $ git add . - $ git commit -m "Initial commit" - -On the central server, initialize a 'bare repository' in some directory: - - $ mkdir proj.git - $ cd proj.git - $ git --bare init - $ touch proj.git/git-daemon-export-ok - -Start the Git daemon if necessary: - - $ git daemon --detach # it may already be running - -For Git hosting services, follow the instructions to setup the initially -empty Git repository. Typically one fills in a form on a webpage. - -'Push' your project to the central server with: - - $ git push central.server/path/to/proj.git HEAD - -To check out the source, a developer types: - - $ git clone central.server/path/to/proj.git - -After making changes, the developer saves changes locally: - - $ git commit -a - -To update to the latest version: - - $ git pull - -Any merge conflicts should be resolved then committed: - - $ git commit -a - -To check in local changes into the central repository: - - $ git push - -If the main server has new changes due to activity by other developers, the -push fails, and the developer should pull the latest version, resolve any merge conflicts, then try again. - -Developers must have SSH access for the above pull and push commands. -However, anyone can see the source by typing: - - $ git clone git://central.server/path/to/proj.git - -The native git protocol is like HTTP: there is no authentication, so anyone can -retrieve the project. Accordingly, by default, pushing is forbidden via the git -protocol. - -=== Secret Source === - -For a closed-source project, omit the touch command, and ensure you never -create a file named `git-daemon-export-ok`. The repository can no longer be -retrieved via the git protocol; only those with SSH access can see it. If all -your repos are closed, running the git daemon is unnecessary because all -communication occurs via SSH. - -=== Bare repositories === - -A bare repository is so named because it has no working directory; it only contains files that are normally hidden away in the `.git` subdirectory. In other words, it maintains the history of a project, and never holds a snapshot of any given version. - -A bare repository plays a role similar to that of the main server in a -centralized version control system: the home of your project. Developers clone -your project from it, and push the latest official changes to it. Typically it -resides on a server that does little else but disseminate data. Development -occurs in the clones, so the home repository can do without a working -directory. - -Many Git commands fail on bare repositories unless the `GIT_DIR` environment variable is set to the repository path, or the `--bare` option is supplied. - -=== Push versus pull === - -Why did we introduce the push command, rather than rely on the familiar pull -command? Firstly, pulling fails on bare repositories: instead you must 'fetch', -a command we later discuss. But even if we kept a normal repository on the -central server, pulling into it would still be cumbersome. We would have to -login to the server first, and give the pull command the network address of the -machine we're pulling from. Firewalls may interfere, and what if we have no -shell access to the server in the first place? - -However, apart from this case, we discourage pushing into a repository, because confusion can ensue when the destination has a working directory. - -In short, while learning Git, only push when the target is a bare repository; otherwise pull. - -=== Forking a Project === - -Sick of the way a project is being run? Think you could do a better job? Then on your server: - - $ git clone git://main.server/path/to/files - -Next, tell everyone about your fork of the project at your server. - -At any later time, you can merge in the changes from the original project with: - - $ git pull - -=== Ultimate Backups === - -Want numerous tamper-proof geographically diverse redundant archives? If your project has many developers, don't do anything! Every clone of your code is effectively a backup. Not just of the current state, but of your project's entire history. Thanks to cryptographic hashing, if anyone's clone becomes corrupted, it will be spotted as soon as they try to communicate with others. - -If your project is not so popular, find as many servers as you can to host clones. - -The truly paranoid should always write down the latest 20-byte SHA1 hash of the HEAD somewhere safe. It has to be safe, not private. For example, publishing it in a newspaper would work well, because it's hard for an attacker to alter every copy of a newspaper. - -=== Light-Speed Multitask === - -Say you want to work on several features in parallel. Then commit your project and run: - - $ git clone . /some/new/directory - -Thanks to http://en.wikipedia.org/wiki/Hard_link[hardlinking], local clones -require less time and space than a plain backup. - -You can now work on two independent features simultaneously. For example, you -can edit one clone while the other is compiling. At any time, you can commit -and pull changes from the other clone: - - $ git pull /the/other/clone HEAD - -=== Guerilla Version Control === - -Are you working on a project that uses some other version control system, and you sorely miss Git? Then initialize a Git repository in your working directory: - - $ git init - $ git add . - $ git commit -m "Initial commit" - -then clone it: - - $ git clone . /some/new/directory - -Now go to the new directory and work here instead, using Git to your heart's content. Once in a while, you'll want to sync with everyone else, in which case go to the original directory, sync using the other version control system, and type: - - $ git add . - $ git commit -m "Sync with everyone else" - -Then go to the new directory and run: - - $ git commit -a -m "Description of my changes" - $ git pull - -The procedure for giving your changes to everyone else depends on the other version control system. The new directory contains the files with your changes. Run whatever commands of the other version control system are needed to upload them to the central repository. - -Subversion, perhaps the best centralized version control system, is used by countless projects. The *git svn* command automates the above for Subversion repositories, and can also be used to http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[export a Git project to a Subversion repository]. - -=== Mercurial === - -Mercurial is a similar version control system that can almost seamlessly work in tandem with Git. With the `hg-git` plugin, a Mercurial user can losslessly push to and pull from a Git repository. - -Obtain the `hg-git` plugin with Git: - - $ git clone git://github.com/schacon/hg-git.git - -or Mercurial: - - $ hg clone http://bitbucket.org/durin42/hg-git/ - -Sadly, I am unaware of an analogous plugin for Git. For this reason, I advocate Git over Mercurial for the main repository, even if you prefer Mercurial. With a Mercurial project, usually a volunteer maintains a parallel Git repository to accommodate Git users, whereas thanks to the `hg-git` plugin, a Git project automatically accommodates Mercurial users. - -Although the plugin can convert a Mercurial repository to a Git repository by pushing to an empty repository, this job is easier with the `hg-fast-export.sh` script, available from: - - $ git clone git://repo.or.cz/fast-export.git - -To convert, in an empty directory: - - $ git init - $ hg-fast-export.sh -r /hg/repo - -after adding the script to your `$PATH`. - -=== Bazaar === - -We briefly mention Bazaar because it is the most popular free distributed -version control system after Git and Mercurial. - -Bazaar has the advantage of hindsight, as it is relatively young; its designers could learn from mistakes of the past, and sidestep minor historical warts. Additionally, its developers are mindful of portability and interoperation with other version control systems. - -A `bzr-git` plugin lets Bazaar users work with Git repositories to some extent. The `tailor` program converts Bazaar repositories to Git repositories, and can do so incrementally, while `bzr-fast-export` is well-suited for one-shot conversions. - -=== Why I use Git === - -I originally chose Git because I heard it could manage the unimaginably unmanageable Linux kernel source. I've never felt a need to switch. Git has served admirably, and I've yet to be bitten by its flaws. As I primarily use Linux, issues on other platforms are of no concern. - -Also, I prefer C programs and bash scripts to executables such as Python scripts: there are fewer dependencies, and I'm addicted to fast running times. - -I did think about how Git could be improved, going so far as to write my own Git-like tool, but only as an academic exercise. Had I completed my project, I would have stayed with Git anyway, as the gains are too slight to justify using an oddball system. - -Naturally, your needs and wants likely differ, and you may be better off with another system. Nonetheless, you can't go far wrong with Git. +== Klonowanie == + +W starszych systemach kontroli wersji polecenie 'checkout' stanowi standardową operacje pozyskiwania danych. Otrzymasz nią zbiór plików konkretnej wersji. + +W Git i innych rozproszonych systemach standardowo służy temu operacja 'clone'. By pozyskać dane, tworzysz najpierw klon całego repozytorium. Lub inaczej mówiąc, otrzymujesz lustrzane odbicie serwera. Wszystko, co można zrobić w centralnym repozytorium, możesz również robić z klonem. + +=== Synchronizacja komputera === + +W celu ochrony danych mógłbym jeszcze zaakceptować korzystanie z archiwum 'tar', a dla prostej synchronizacji używania *rsync*. Jednak czasami pracując na laptopie, a innym razem na komputerze stacjonarnym, może się zdarzyć, że komputery nie miały możliwości zsynchwonizowania się w międzyczasie. + +Utwórz repozytorium Gita w wykonaj 'commit' twoich danych na jednym z komputerów. Potem na następnym wpisz: + + $ git clone drugi.komputer:/ścieżka/do/danych + +by otrzymać drugą kopie danych i jednocześnie utworzyć repozytorium Gita na drugim komputerze. Od teraz poleceniem: + + $ git commit -a + $ git pull drugi.komputer:/ścieżka/do/danych HEAD + +przenosisz stan drugiego komputera na komputer na którym właśnie pracujesz. Jeśli dokonałaś zmian w tym samym pliku na obydwu komputerach, Git poinformuje cię o tym, po usunięciu konfliktu powinnaś ponowić 'commit'. + +=== Klasyczna kontrola kodu źródłowego === + +Utwórz repozytorium Gita w katalogu roboczym projektu: + + $ git init + $ git add . + $ git commit -m "Pierwszy commit" + +Na centralnym serwerze utwórz gołe ('bare') repozytorium w jakimkolwiek katalogu: + + $ mkdir proj.git + $ cd proj.git + $ git --bare init + $ touch proj.git/git-daemon-export-ok + +W razie konieczności, wystartuj daemon Gita: + + $ git daemon --detach # ponieważ, gdyby uruchomiony był wcześniej + +Jeśli korzystasz z hostingu to poszukaj na stronie usługodawcy wskazówek jak utworzyć repozytorium 'bare'. Zwykle konieczne jest do tego wypełnienie formularza online na jego stronie. + +Popchaj ('push') twój projekt teraz na centralny serwer: + + $ git push centralny.serwer/ścieżka/do/projektu.git HEAD + +By pozyskać kod źródłowy programista podaje zwykle polecenie w rodzaju: + + $ git clone główny.serwer/ścieżka/do/projektu.git + +Po dokonaniu edycji programista zapamiętuje zmiany najpierw lokalnie: + + $ git commit -a + +Aby zaktualizować do wersji istniejącej na głównym serwerze: + + $ git pull + +Jeśli wystąpią jakiekolwiek konflikty łączenia ('merge') , powinny być najpierw usunięte i na nowo zostać wykonany 'commit'. + + $ git commit -a + +Lokalne zmiany przekazujemy do serwera poleceniem: + + $ git push + +Jeśli w międzyczasie nastąpiły nowe zmiany na serwerze wprowadzone przez innego programistę, twój 'push' nie powiedzie się. Zaktualizuj lokalne repozytorium ponownie poleceniem 'pull', pozbądź się konfliktów i spróbuj jeszcze raz. + +Programiści potrzebują dostępu poprzez SSH dla wykonania poleceń 'pull' i 'push'. Mimo to, każdy może skopiowć kod źródłowy poprzez podanie: + + $ git clone git://główny.serwer/ścieżka/do/projektu.git + +Protokół GIT przypomina HTTP: nie posiada autentyfikacji, a więc każdy może skopiować dane. Przy ustawieniach standardowych wykonanie 'push' za pomocą protokołu GIT jest zdezaktywowane. + +=== Utajnienie źródła === + +Przy projektach Closed-Source wyklucz używanie poleceń takich jak 'clone' i upewnij się, że nie został utworzony plik o nazwie 'git-daemon-export-ok'. Wtedy repozytorium nie może już komunikować się poprzez protokół 'git', tylko posiadający dostęp przez SHH mogą widzieć dane. Jeśli wszystkie repozytoria są zamknięte, nie ma potrzeby startować daemona 'git', ponieważ cała komunikacja odbywa się wyłącznie za pomocą SSH. + +=== Gołe repozytoria === + +Określenie: 'gołe' ('bare') repozytorium, powstało, ze względu na fakt, iż repozytorium to nie posiada katalogu roboczego. Posiada jedynie dane, które są zwykle schowane w podkatalogu '.git'. Innymi słowy: zarządza historią projektu, nie posiada jednak katalogu roboczego jakiejkolwiek wersji. + +Repozytorium 'bare' przejmuje rolę podobną do roli głównego serwera w scentralizowanych systemach kontroli wersji: dach twojego projektu. Programiści klonują twój projekt stamtąd i przesyłają tam ostatnie oficjalne zmiany. Często znajduje się ono na serwerze, którego jedynym zadaniem jest wyłącznie dystrybucja danych. Sama praca nad projektem przebiega na jego klonach, w ten sposób główne repozytorium daje sobie radę nie korzystając z katalogu roboczego. + +Wiele z poleceń Gita nie będzie funkcjonować w repozytoriach 'bare', chyba że ustawimy zmienną systemową GIT_DIR na katalog roboczy repozytorium albo przekażemy opcję `--bare`. + +=== 'Push', czy 'pull'? === + +Dlaczego wprowadziliśmy polecenie 'push', a nie pozostaliśmy przy znanym nam już 'pull'? Po pierwsze, 'pull' nie działa z repozytoriami 'bare': zamiast niego używaj 'fetch', polecenia którym zajmiemy się później. Również gdybyśmy nawet używali normalnego repozytorium na serwerze centralnym, polecenie 'pull' byłoby raczej niewygodne. Musielibyśmy wpierw zalogować się na serwerze i przekazać poleceniu 'pull' adres IP komputera z którego chcemy ściągnąć pliki. Jeśli w ogóle posiadalibyśmy dostęp do konsoli serwera, to prawdopodobnie przeszkodziłaby nam firewalle na drodze do naszego komputera. + +W każdym bądź razie, nawet jeśli nawet mogło by to zadziałać, odradzam z korzystania z funkcji 'push' w ten sposób. Poprzez samo posiadanie katalogu roboczego na serwerze mogłoby powstać wiele nieścisłości. + +Krótko mówiąc, podczas gdy uczysz się korzystania z Git, korzystaj z polecenia 'push' tylko, gdy celem jest repozytorium 'bare', w wszystkich innych wypadkach z 'pull'. + +=== Rozwidlenie projektu === + +Jeśli nie potrafisz już patrzeć na kierunek rozwoju w jakim poszedł jakiś projekt. Uważasz, że potrafisz to lepiej. To po prostu utwórz jego 'fork', w tym celu na twoim serwerze podaj: + + $ git clone git://główny.serwer/ścieżka/do/danych + +Następnie, poinformuj wszystkich o nowym 'forku' projektu na twoim serwerze. + +W każdej późniejszej chwili możesz dokonać łączenia ('merge') zmian z oryginalnego projektu, poprzez: + + $ git pull + +=== Ultymatywny backup === + +Chcesz posiadać liczne, wolne od manipulacji, redundantne kopie bezpieczeństwa w różnych miejscach? Jeśli projekt posiada wielu programistów, nie musisz niczego robić. Ponieważ każdy klon twojego kodu jest pełnowartościową kopią bezpieczeństwa. Nie tylko jego aktualna wersja, lecz również cała historia projektu. Gdy jakikolwiek klon zostanie uszkodzony, dzięki kryptograficznemu hashowaniu, zostanie to natychmiast rozpoznane, jeśli tylko dana osoba będzie próbować wymiany z innymi. + +Jeśli twój projekt nie jest jeszcze wystarczająco znany, spróbuj pozyskać tak wiele serwerów, ile to możliwe, by umieścić tam jego klon. + +Ci najbardziej paranoidalni powinni zawsze zapisywać 20 bajtów ostatniej sumy kontrolnej SHA1 dla HEAD i przechowywać w bezpiecznym miejscu. Musi być bezpieczny, jednak nie tajny. Na przykład opublikowanie go w gazecie dobrze by spełniło swoje zadanie, dość trudnym zadaniem byłoby zmanipulowanie każdej kopii gazety. + +=== Wielozadaniowość z prędkością światła === + +Załóżmy, że chcesz pracować nad kilkoma funkcjami równocześnie. Wykonaj 'commit' i wpisz: + + $ git clone . /jakiś/nowy/katalog + +Za sprawą http://en.wikipedia.org/wiki/Hard_link[twardych linków] stworzenie lokalnej kopii zajmuje dużo mniej czasu i pamięci niż zwykły backup. + +Możesz pracować nad dwoma niezależnymi funkcjami jednocześnie. Na przykład, możesz pracować nad jednym klonem dalej, podczas gdy drugi jest właśnie kompilowany. W każdym momencie możesz wykonać 'commit' i 'pull' innego klonu. + + $ git pull /inny/klon HEAD + +=== Kontrola wersji z podziemia === + +Pracujesz nad projektem, który używa innego systemu kontroli wersji i tęsknisz za Gitem? Utwórz po prostu repozytorium Gita w twoim katalogu roboczym: + + $ git init + $ git add . + $ git commit -m "Pierwszy commit" + +następnie sklonuj go: + + $ git clone . /jakiś/inny/katalog + +Przejdź teraz do nowego katalogu i pracuj według upodobania. Kiedyś zechcesz zsynchronizować pracę, idź do oryginalnego katalogu, zaktualizuj go najpierw z tym innym systemem kontroli wersji, następnie wpisz: + + $ git add . + $ git add . $ git commit -m"Synchronizacja z innym systemem kontroli wersji" + +Teraz przejdź do nowego katalogu i podaj: + + $ git commit -a -m "Opis zmian" + $ git pull + +Sposób w jaki przekażesz zmiany drugiemu systemowi zależy już od jego sposobu działania. Twój nowy katalog posiada dane ze zmianami przez ciebie wprowadzonymi. Wykonaj jeszcze adekwatne kroki żądane przez ten inny system kontroli wersji, by przekazać dane do centralnego repozytorium. + +Subversion, być może najlepszy z centralnych systemów, stosowany jest w wielu projektach. Komenda *git svn* automatyzuje powyższe kroki, może być użyta na przykład do http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[exportu projektu Git do repozytorium Subversion]. + +=== Mercurial === + +Mercurial to podobny do Gita system kontroli wersji, który prawie bezproblemowo potrafi pracować z Gitem. Korzystając z rozszerzenia `hg-git` użytkownik Mercurial jest w stanie bez strat wykonywać 'push' i 'pull' z repozytorium Gita. + +Możesz ściągnąć sobie rozszerzenie `hg-git` za pomocą Gita: + + $ git clone git://github.com/schacon/hg-git.git + +albo za pomocą Mercurial: + + $ hg clone http://bitbucket.org/durin42/hg-git/ + +Niestety nie są mi znane takie rozszerzenia dla Gita. Dlatego jestem za używaniem Gita jako głównego repozytorium, nawet gdy preferujesz Mercurial. W projektach prowadzonych za pomocą Mercurial często znajdziemy woluntariusza, który równolegle prowadzi repozytorium Gita, dzięki pomocy rozszerzenia `hg-git` projekty Gita automatycznie osiągają użytkowników Mercurial. + +To rozszerzenie potrafi również zmienić skład Mercurial w skład Gita, za pomocą komendy 'push' do gołego repozytorium Gita. Jeszcze łatwiej dokonamy tego skryptem `hg-fast-export.sh`, który możemy tu znaleźć: + + $ git clone git://repo.or.cz/fast-export.git + +Aby skonwertować, wejdź do pustego katalogu: + + $ git init + $ hg-fast-export.sh -r /hg/repo + +po uprzednim dodaniu skryptu do twojego `$ PATH`. + +=== Bazaar === + +Wspomnijmy również pokrótce o Bazaar, ponieważ jest to najbardziej popularny darmowy rozproszony system kontroli wersji po Git i Mercurial. + +Bazar będąc stosunkowo młodym systemem, posiada zaletę perspektywy czasu, jego twórcy mogli uczyć się na błędach z przeszłości i uniknąć historycznych naleciałości. Poza tym programiści byli świadomi popularności i wagi interakcji z innymi systemami kontroli wersji. + +Rozszerzenie `Bzr-git` pozwala użytkownikom Bazar dość łatwo pracować z repozytoriami Gita. Program `tailor` konwertuje składy Bazaar do składów Gita i może robić to na bieżąco, podczas gdy `bzr-fast-export` lepiej nadaje się do jednorazowej konwersji. + +=== Dlaczego korzystam z Gita === + +Zdecydowałem się pierwotnie do wyboru Gita, ponieważ słyszałem, że jest w stanie zarządzać tak zawiłym i rozległym projektem jak kod źródłowy Linuksa. Jak na razie nie miałem powodów do zmiany. Git służył mi znakomicie i do tej pory mnie nie zawiódł. Ponieważ w pierwszej linii pracuję na Linuksie, problemy innych platform nie mają dla mnie znaczenia. + +Preferuję również programy 'C' i skrypty 'bash' w opozycji do na przykład Pythona: posiadają mniej zależności, wolę też, gdy kod jest wykonywany szybko. + +Myślałem już też nad tym, jak można by ulepszyć Gita, poszło to nawet tak daleko, że napisałem własną aplikacje podobną do niego, w celu jednak wyłącznie ćwiczeń akademickich. Nawet gdybym zakończył mój projekt, mimo to pozostałbym przy Git, bo ulepszenia byłyby zbyt minimalne by uzasadnić zastosowanie odosobnionego systemu. + +Oczywiście może się okazać, że twoje potrzeby i oczekiwania są zupełnie inne i być może wygodniej jest tobie z zupełnie innym systemem. Jakby jednak nie spojrzeć, stosując Git nie popełnisz tu niczego złego. diff --git a/pl/drawbacks.txt b/pl/drawbacks.txt dissimilarity index 98% index eab2668..2c65d41 100644 --- a/pl/drawbacks.txt +++ b/pl/drawbacks.txt @@ -1,97 +1,91 @@ -== Appendix A: Git Shortcomings == - -There are some Git issues I've swept under the carpet. Some can be handled easily with scripts and hooks, some require reorganizing or redefining the project, and for the few remaining annoyances, one will just have to wait. Or better yet, pitch in and help! - -=== SHA1 Weaknesses === - -As time passes, cryptographers discover more and more SHA1 weaknesses. Already, -finding hash collisions is feasible for well-funded organizations. Within -years, perhaps even a typical PC will have enough computing power to silently -corrupt a Git repository. - -Hopefully Git will migrate to a better hash function before further research -destroys SHA1. - -=== Microsoft Windows === - -Git on Microsoft Windows can be cumbersome: - -- http://cygwin.com/[Cygwin], a Linux-like environment for Windows, contains http://cygwin.com/packages/git/[a Windows port of Git]. - -- http://code.google.com/p/msysgit/[Git on MSys] is an alternative requiring minimal runtime support, though a few of the commands need some work. - -=== Unrelated Files === - -If your project is very large and contains many unrelated files that are constantly being changed, Git may be disadvantaged more than other systems because single files are not tracked. Git tracks changes to the whole project, which is usually beneficial. - -A solution is to break up your project into pieces, each consisting of related files. Use *git submodule* if you still want to keep everything in a single repository. - -=== Who's Editing What? === - -Some version control systems force you to explicitly mark a file in some way before editing. While this is especially annoying when this involves talking to a central server, it does have two benefits: - - 1. Diffs are quick because only the marked files need be examined. - - 2. One can discover who else is working on the file by asking the central server who has marked it for editing. - -With appropriate scripting, you can achieve the same with Git. This requires cooperation from the programmer, who should execute particular scripts when editing a file. - -=== File History === - -Since Git records project-wide changes, reconstructing the history of a single file requires more work than in version control systems that track individual files. - -The penalty is typically slight, and well worth having as other operations are incredibly efficient. For example, `git checkout` is faster than `cp -a`, and project-wide deltas compress better than collections of file-based deltas. - -=== Initial Clone === - -Creating a clone is more expensive than checking out code in other version control systems when there is a lengthy history. - -The initial cost is worth paying in the long run, as most future operations will then be fast and offline. However, in some situations, it may be preferable to create a shallow clone with the `--depth` option. This is much faster, but the resulting clone has reduced functionality. - -=== Volatile Projects === - -Git was written to be fast with respect to the size of the changes. Humans make small edits from version to version. A one-liner bugfix here, a new feature there, emended comments, and so forth. But if your files are radically different in successive revisions, then on each commit, your history necessarily grows by the size of your whole project. - -There is nothing any version control system can do about this, but standard Git users will suffer more since normally histories are cloned. - -The reasons why the changes are so great should be examined. Perhaps file formats should be changed. Minor edits should only cause minor changes to at most a few files. - -Or perhaps a database or backup/archival solution is what is actually being sought, not a version control system. For example, version control may be ill-suited for managing photos periodically taken from a webcam. - -If the files really must be constantly morphing and they really must be versioned, a possibility is to use Git in a centralized fashion. One can create shallow clones, which checks out little or no history of the project. Of course, many Git tools will be unavailable, and fixes must be submitted as patches. This is probably fine as it's unclear why anyone would want the history of wildly unstable files. - -Another example is a project depending on firmware, which takes the form of a huge binary file. The history of the firmware is uninteresting to users, and updates compress poorly, so firmware revisions would unnecessarily blow up the size of the repository. - -In this case, the source code should be stored in a Git repository, and the binary file should be kept separately. To make life easier, one could distribute a script that uses Git to clone the code, and rsync or a Git shallow clone for the firmware. - -=== Global Counter === - -Some centralized version control systems maintain a positive integer that increases when a new commit is accepted. Git refers to changes by their hash, which is better in many circumstances. - -But some people like having this integer around. Luckily, it's easy to write scripts so that with every update, the central Git repository increments an integer, perhaps in a tag, and associates it with the hash of the latest commit. - -Every clone could maintain such a counter, but this would probably be useless, since only the central repository and its counter matters to everyone. - -=== Empty Subdirectories === - -Empty subdirectories cannot be tracked. Create dummy files to work around this problem. - -The current implementation of Git, rather than its design, is to blame for this drawback. With luck, once Git gains more traction, more users will clamour for this feature and it will be implemented. - -=== Initial Commit === - -A stereotypical computer scientist counts from 0, rather than 1. Unfortunately, with respect to commits, git does not adhere to this convention. Many commands are unfriendly before the initial commit. Additionally, some corner cases must be handled specially, such as rebasing a branch with a different initial commit. - -Git would benefit from defining the zero commit: as soon as a repository is constructed, HEAD would be set to the string consisting of 20 zero bytes. This special commit represents an empty tree, with no parent, at some time predating all Git repositories. - -Then running git log, for example, would inform the user that no commits have been made yet, instead of exiting with a fatal error. Similarly for other tools. - -Every initial commit is implicitly a descendant of this zero commit. - -However there are some problem cases unfortunately. If several branches with different initial commits are merged together, then rebasing the result requires substantial manual intervention. - -=== Interface Quirks === - -For commits A and B, the meaning of the expressions "A..B" and "A...B" depends -on whether the command expects two endpoints or a range. See *git help diff* -and *git help rev-parse*. +== Załącznik A: Niedociągnięcia Gita == + +O kilku problemach mogących wystąpić z Gitem nie wspomniałem do tej pory. Niektóre z nich można łatwo rozwiązać korzystając ze skryptów i 'hooks', inne wymagają reorganizacji i ponownego zdefiniowania całego projektu, a na rozwiązanie kilku innych uniedogodnień możesz tylko uzbroić się w cierpliwość i czekać na ich usunięcie. Albo jeszcze lepiej, samemu się nimi zająć i spróbować pomóc. + +=== Słabości SHA1 === + +Z biegiem czasu kryptografowie odkrywają coraz więcej słabości systemu SHA1. Już dzisiaj byłoby możliwe dla przedsięwzięć dysponujących odpowiednimi zasobami finansowymi znaleźć kolizje w hashach. Za kilka lat, całkiem możliwe, że normalny domowy PC będzie dysponował odpowiednim zasobem mocy obliczeniowej, by skorumpować niepostrzeżenie repozytorium Gita. + +Miejmy nadzieję, że Git przestawi się na lepszą funkcje hashującą, zanim badania nad SHA1 zrobią go bezużytecznym. + +=== Microsoft Windows === + +Korzystanie z Gita pod Microsoft Windows może być frustrujące: + +- http://cygwin.com/[Cygwin], unixoidalne środowisko dla Windowsa posiada http://cygwin.com/packages/git/[port Gita]. + +- http://code.google.com/p/msysgit/[Git dla MSys] jest jedną z alternatyw, niektóre polecenia wymagają jednak poprawek. + +=== Pliki niepowiązane === + +Jeśli twój projekt jest bardzo duży i zawiera wiele plików, które nie są bezpośrednio ze sobą związane, mimo to jednak często zostają zmieniane, Git może tu działać gorzej niż inne systemy, ponieważ nie prowadzi monitoringu poszczególnych plików. Git kontroluje zawsze całość projektu, co w normalnym wypadku jest zaletą. + +Jednym z możliwych rozwiązań mogłoby być podzielenie twojego projektu na kilka mniejszych, w których znajdują się jedynie pliki od siebie zależne. Korzystaj z *git submodule* jeśli mimo to chcesz cały twój projekt mieć w tym samym repozytorium. + +=== Kto nad czym pracuje? === + +Niektóre systemy kontroli wersji zmuszają cię, by w jakiś sposób oznaczyć pliki nad którymi pracujesz. Mimo że jest to bardzo uciążliwe, gdyż wymaga ciągłej komunikacji z serwerem centralnym, posiada to też swoje zalety: + +1. Różnice zostają szybko znalezione, ponieważ wystarczy skontrolować wyłącznie oznaczone pliki. + +2. Każdy może szybko sprawdzić, kto aktualnie nad czym pracuje, sprawdzając na serwerze po prostu kto zaznaczył jakie dane do edycji. + +Używając odpowiednich skryptów uda ci się to również przy pomocy Gita. Wymaga to jednak współdziałania programistów, ponieważ muszą również korzystać z tych skryptów podczas pracy nad projektem. + +=== Historia pliku === + +Ponieważ Git loguje zmiany tylko dla całości projektu jako takiego, rekonstrukcja przebiegu zmian pojedynczego pliku jest bardziej pracochłonna, niż w innych systemach kontrolujących pojedyncze . + +Te wady są w większości przypadków uznawane za marginalne i nie są brane pod uwagę, ponieważ inne operacje są bardzo wydajne. Na przykład polecenie `git checkout` jest szybsze niż `cp -a`, zmiany w zakresie całego projektu daje się lepiej komprymować niż zbiór zmian na bazie pojedynczych plików. + +=== Pierwszy klon === + +Wykonanie klonu jest kosztowniejsze niż w innych systemach kontroli wersji jeśli istnieje długa historia. + +Początkowy koszt spłaca się jednak na dłuższą metę ponieważ większość przyszłych operacji przeprowadzane będzie szybko i offline. Niemniej jednak istnieją sytuacje, w których lepiej utworzyć powierzchowny klon korzystając z opcji `--depth`. Trwa to o wiele krócej, taki klon jednak posiada też tylko ograniczoną funkcjonalność. + +=== Niestałe projekty === + +Git został napisany z myślą optymalizacji prędkości działania przy dokonywaniu wielkich zmian. Ludzie robią jednak pomniejsze zmiany z wersji na wersję. Jakaś poprawka tutaj, jakaś nowa funkcja gdzie indziej, poprawienie komentarzy, itd. Ale jeśli twoje dane znacznie się od siebie różnią pomiędzy następującymi po sobie wersjami, to chcąc nie chcąc przy każdym 'commit' projekt zwiększy się o te zmiany. + +Nie wymyślono jednak do tej pory niczego w żadnym systemie kontroli wersji, by móc temu zapobiec, tutaj jednak użytkownik Gita cierpi najbardziej, ponieważ w normalnym wypadku klonuje cały przebieg projektu. + +Powinno się w takim wypadku szukać powodów wystąpienia największych zmian. Ewentualnie można czasami zmienić format danych. Małe zmiany w projekcie powinny pociągać tylko minimalne zmiany na tak wąskiej grupie plików, jak to tylko możliwe. + +Może czasami bardziej wskazana byłaby baza danych, czy jakiś system archiwizacji zamiast systemu kontroli wersji. Na przykład nie jest dobrym sposobem zastosowanie systemu kontroli wersji do zarządzania zdjęciami wykonywanymi periodycznie przez kamerę internetową. + +Jeśli dane ulegają ciągłym zmianom i naprawdę muszą być objęte kontrolą wersji, jedną z możliwości jest zastosowanie Gita w scentralizowanej formie. Każdy może dokonywać pobieżnych klonów, które mało co lub wcale nie mają nic do czynienia z przebiegiem projektu. Oczywiście w takim wypadku wiele funkcji Gita nie będzie dostępnych a zmiany muszą być przekazywane w formie 'patch'. Prawdopodobnie będzie to dość dobrze działać, mimo iż nie jest do końca jasne komu potrzebna jest znajomość przebiegu tak ogromnej ilości niestabilnych danych. + +Innym przykładem może być projekt, który zależny jest od firmware przyjmującej kształt wielkiej danej w formie binarnej. Historia pliku firmware nie interesuje użytkownika, a zmiany nie pozwalają się wygodnie komprymować, wielkość repozytorium wzrasta niepotrzebnie o nowe wersje binarnego pliku firmware. + +W takim wypadku należałoby trzymać w repozytorium wyłącznie kod źródłowy, a sam plik binarny poza nim. By ułatwić sobie życie, ktoś mógłby opracować skrypt, który Git wykorzystuje do klonowania kodu źródłowego i 'rsync' albo pobieżny klon dla samego firmware. + +=== Licznik globalny === + +Wiele systemów kontroli wersji udostępnia licznik, który jest zwiększany z każdym "commit". Git natomiast odwołuje się przy zmianach do sum kontrolnych SHA1, co w wielu przypadkach jest lepszym rozwiązaniem. + +Niektórzy jednak przyzwyczaili się do tego licznika. Na szczęście, łatwo jest napisać skrypt zwiększający stan licznika przy każdej aktualizacji centralnego repozytorium Gita. Może w formie taga, który powiązany jest z sumą kontrolną ostatniego 'commit'. + +Każdy klon mógłby posiadać taki licznik, jednak byłby on prawdopodobnie bezużyteczny, ponieważ tylko licznik centralnego repozytoriom ma znaczenie. + +=== Puste katalogi === + +Nie ma możliwości kontroli wersji pustych katalogów. Aby obejść ten problem wystarczy utworzyć w takim katalogu plik dummy. + +To raczej obecna implementacja Gita, a mniej jego konstrukcja, jest odpowiedzialna za to wadę. Przy odrobinie szczęścia, jeśli Git jeszcze bardziej się upowszechni i więcej użytkowników żądać będzie tej funkcji, to być może zostanie zaimplementowana. + +=== Pierwszy 'commit' === + +Stereotypowy informatyk liczy od 0 zamiast 1. Niestety, w kwestii 'commits' Git nie podąża za tą konwencją. Wiele komend marudzi przed wykonaniem pierwszego 'commit'. Dodatkowo, różnego rodzaju krańcowe przypadki muszą być traktowane specjalnie, jak 'rebase' dla 'branch' o różniącym się pierwszym 'commit'. + +Git zyskałby na zdefiniowaniu tzw. 'zero-commit', ponieważ zaraz po zainicjowaniu repozytorium, 'HEAD' otrzymałby 20 bajtowy hash SHA1. Ten specjalny 'commit' reprezentowałby puste drzewo, bez rodziców, być może pradziad wszystkich repozytoriów. + +Jeśli na przykład użytkownik wykonałby polecenie *git log*, zostałby poinformowany, że nie istnieje jeszcze żaden 'commit', gdzie na dzień dzisiejszy taka komenda wywoła błąd. Analogicznie dzieje się też z innymi poleceniami. + +Każdy inicjujący 'commit' byłby pochodną tego zerowego 'commit'. + +Niestety występuje jeszcze kilka innych problemów. Jeśli chcemy scalić kilka 'branches' o różniących sie inicjalnych 'commits' i przeprowadzić 'rebase', musimy ingerować ręcznie. + +=== Charakterystyka zastosowania === + +Dla 'commits' A i B, znaczenie wyrażeń "A..B" i "A...B" zależy od tego, czy polecenie oczekuje dwóch punktów końcowych, czy zakresu. Sprawdź *git help diff* i *git help rev-parse*. diff --git a/pl/grandmaster.txt b/pl/grandmaster.txt dissimilarity index 95% index 18df2b7..2854423 100644 --- a/pl/grandmaster.txt +++ b/pl/grandmaster.txt @@ -1,232 +1,179 @@ -== Git Grandmastery == - -By now, you should be able to navigate the *git help* pages and understand -almost everything. However, pinpointing the exact command required to solve a -given problem can be tedious. Perhaps I can save you some time: below are some -recipes I have needed in the past. - -=== Source Releases === - -For my projects, Git tracks exactly the files I'd like to archive and release -to users. To create a tarball of the source code, I run: - - $ git archive --format=tar --prefix=proj-1.2.3/ HEAD - -=== Commit What Changed === - -Telling Git when you've added, deleted and renamed files is troublesome for -certain projects. Instead, you can type: - - $ git add . - $ git add -u - -Git will look at the files in the current directory and work out the details by -itself. Instead of the second add command, run `git commit -a` if you also -intend to commit at this time. See *git help ignore* for how to specify files -that should be ignored. - -You can perform the above in a single pass with: - - $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove - -The *-z* and *-0* options prevent ill side-effects from filenames containing -strange characters. As this command adds ignored files, you may want to use the -`-x` or `-X` option. - -=== My Commit Is Too Big! === - -Have you neglected to commit for too long? Been coding furiously and forgotten -about source control until now? Made a series of unrelated changes, because -that's your style? - -No worries. Run: - - $ git add -p - -For each edit you made, Git will show you the hunk of code that was changed, -and ask if it should be part of the next commit. Answer with "y" or "n". You -have other options, such as postponing the decision; type "?" to learn more. - -Once you're satisfied, type - - $ git commit - -to commit precisely the changes you selected (the 'staged' changes). Make sure -you omit the *-a* option, otherwise Git will commit all the edits. - -What if you've edited many files in many places? Reviewing each change one by -one becomes frustratingly mind-numbing. In this case, use *git add -i*, whose -interface is less straightforward, but more flexible. With a few keystrokes, -you can stage or unstage several files at a time, or review and select changes -in particular files only. Alternatively, run *git commit \--interactive* which -automatically commits after you're done. - -=== The Index: Git's Staging Area === - -So far we have avoided Git's famous 'index', but we must now confront it to -explain the above. The index is a temporary staging area. Git seldom shuttles -data directly between your project and its history. Rather, Git first writes -data to the index, and then copies the data in the index to its final -destination. - -For example, *commit -a* is really a two-step process. The first step places a -snapshot of the current state of every tracked file into the index. The second -step permanently records the snapshot now in the index. Committing without the -*-a* option only performs the second step, and only makes sense after running -commands that somehow change the index, such as *git add*. - -Usually we can ignore the index and pretend we are reading straight from and writing straight to the history. On this occasion, we want finer control, so we manipulate the index. We place a snapshot of some, but not all, of our changes into the index, and then permanently record this carefully rigged snapshot. - -=== Don't Lose Your HEAD === - -The HEAD tag is like a cursor that normally points at the latest commit, advancing with each new commit. Some Git commands let you move it. For example: - - $ git reset HEAD~3 - -will move the HEAD three commits back. Thus all Git commands now act as if you hadn't made those last three commits, while your files remain in the present. See the help page for some applications. - -But how can you go back to the future? The past commits know nothing of the future. - -If you have the SHA1 of the original HEAD then: - - $ git reset 1b6d - -But suppose you never took it down? Don't worry: for commands like these, Git saves the original HEAD as a tag called ORIG_HEAD, and you can return safe and sound with: - - $ git reset ORIG_HEAD - -=== HEAD-hunting === - -Perhaps ORIG_HEAD isn't enough. Perhaps you've just realized you made a monumental mistake and you need to go back to an ancient commit in a long-forgotten branch. - -By default, Git keeps a commit for at least two weeks, even if you ordered -Git to destroy the branch containing it. The trouble is finding the appropriate -hash. You could look at all the hash values in `.git/objects` and use trial -and error to find the one you want. But there's a much easier way. - -Git records every hash of a commit it computes in `.git/logs`. The subdirectory `refs` contains the history of all activity on all branches, while the file `HEAD` shows every hash value it has ever taken. The latter can be used to find hashes of commits on branches that have been accidentally lopped off. - -The reflog command provides a friendly interface to these log files. Try - - $ git reflog - -Instead of cutting and pasting hashes from the reflog, try: - - $ git checkout "@{10 minutes ago}" - -Or checkout the 5th-last visited commit via: - - $ git checkout "@{5}" - -See the ``Specifying Revisions'' section of *git help rev-parse* for more. - -You may wish to configure a longer grace period for doomed commits. For -example: - - $ git config gc.pruneexpire "30 days" - -means a deleted commit will only be permanently lost once 30 days have passed -and *git gc* is run. - -You may also wish to disable automatic invocations of *git gc*: - - $ git config gc.auto 0 - -in which case commits will only be deleted when you run *git gc* manually. - -=== Building On Git === - -In true UNIX fashion, Git's design allows it to be easily used as a low-level component of other programs, such as GUI and web interfaces, alternative command-line interfaces, patch managements tools, importing and conversion tools and so on. In fact, some Git commands are themselves scripts standing on the shoulders of giants. With a little tinkering, you can customize Git to suit your preferences. - -One easy trick is to use built-in Git aliases to shorten your most frequently -used commands: - - $ git config --global alias.co checkout - $ git config --global --get-regexp alias # display current aliases - alias.co checkout - $ git co foo # same as 'git checkout foo' - -Another is to print the current branch in the prompt, or window title. -Invoking - - $ git symbolic-ref HEAD - -shows the current branch name. In practice, you most likely want to remove -the "refs/heads/" and ignore errors: - - $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- - -The +contrib+ subdirectory is a treasure trove of tools built on Git. -In time, some of them may be promoted to official commands. On Debian and -Ubuntu, this directory lives at +/usr/share/doc/git-core/contrib+. - -One popular resident is +workdir/git-new-workdir+. Via clever symlinking, this script creates a new working directory whose history is shared with the original repository: - - $ git-new-workdir an/existing/repo new/directory - -The new directory and the files within can be thought of as a clone, except since the history is shared, the two trees automatically stay in sync. There's no need to merge, push, or pull. - -=== Daring Stunts === - -These days, Git makes it difficult for the user to accidentally destroy data. -But if you know what you are doing, you can override safeguards for common -commands. - -*Checkout*: Uncommitted changes cause checkout to fail. To destroy your changes, and checkout a given commit anyway, use the force flag: - - $ git checkout -f HEAD^ - -On the other hand, if you specify particular paths for checkout, then there are no safety checks. The supplied paths are quietly overwritten. Take care if you use checkout in this manner. - -*Reset*: Reset also fails in the presence of uncommitted changes. To force it through, run: - - $ git reset --hard 1b6d - -*Branch*: Deleting branches fails if this causes changes to be lost. To force a deletion, type: - - $ git branch -D dead_branch # instead of -d - -Similarly, attempting to overwrite a branch via a move fails if data loss would ensue. To force a branch move, type: - - $ git branch -M source target # instead of -m - -Unlike checkout and reset, these two commands defer data destruction. The -changes are still stored in the .git subdirectory, and can be retrieved by -recovering the appropriate hash from `.git/logs` (see "HEAD-hunting" above). -By default, they will be kept for at least two weeks. - -*Clean*: Some git commands refuse to proceed because they're worried about -clobbering untracked files. If you're certain that all untracked files and -directories are expendable, then delete them mercilessly with: - - $ git clean -f -d - -Next time, that pesky command will work! - -=== Preventing Bad Commits === - -Stupid mistakes pollute my repositories. Most frightening are missing files due -to a forgotten *git add*. Lesser transgressions are trailing whitespace and -unresolved merge conflicts: though harmless, I wish these never appeared on the -public record. - -If only I had bought idiot insurance by using a _hook_ to alert me about these problems: - - $ cd .git/hooks - $ cp pre-commit.sample pre-commit # Older Git versions: chmod +x pre-commit - -Now Git aborts a commit if useless whitespace or unresolved merge conflicts are -detected. - -For this guide, I eventually added the following to the beginning of the -*pre-commit* hook to guard against absent-mindedness: - - if git ls-files -o | grep '\.txt$'; then - echo FAIL! Untracked .txt files. - exit 1 - fi - -Several git operations support hooks; see *git help hooks*. We activated the -sample *post-update* hook earlier when discussing Git over HTTP. This runs -whenever the head moves. The sample post-update script updates files Git needs -for communication over Git-agnostic transports such as HTTP. +== Git dla zaawansowanych == + +W międzyczasie powinnaś umieć odnaleźć się na stronach *git help* i rozumieć większość zagadnień. Mimo to może okazać się dość mozolne odnalezienie odpowiedniej komendy dla rozwiązania pewnego zadania. Może uda mi się zaoszczędzić ci trochę czasu: poniżej znajdziesz kilka przepisów, które były mi przydatne w przeszłości. + +=== Publikowanie kodu źródłowego === + +Git zarządza w moich projektach dokładnie tymi danymi, które chcę archiwizować i dać do dyspozycji innym użytkownikom. Aby utworzyć archiwum tar kodu źródłowego, używam polecenia: + + $ git archive --format=tar --prefix=proj-1.2.3/ HEAD + +=== Zmiany dla 'commit' === + +Powiadomienie Gita o dodaniu, skasowaniu czy zmianie nazwy plików może okazać sie przy niektórych projektach dość uciążliwą pracą. Zamiast tego można skorzystać z: + + $ git add . + $ git add -u + +Git przyjży się danym w aktualnym katalogu i odpracuje sam szczegóły. Zamiast tego drugiego polecenia możemy użyć `git commit -a`, jeśli i tak mamy zamiar przeprowadzić 'comit'. Sprawdź też *git help ignore*, by dowiedzieć się jak zdefiniować dane, które powinny być zignorowane. + +Można to także wykonać za jednyym zamachem: + + $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove + +Opcje *-z* i *-0* zapobiegą przed niechcianymi efektmi ubocznymi przez niestandardowe znaki w nazwach plików. Ale ponieważ to polecenie dodaje również pliki które powinny być zignorowane, można dodać do niego jeszcze opcje `-x` albo `-X` + +=== Mój 'commit' jest za duży! === + +Od dłuższego czasu nie pamiętałaś o wykonaniu 'commit'? Tak namiętnie programowałaś, że zupełnie zapomniałaś o kontroli kodu źródłowego? Przeprowadzasz serię niezależnych zmian, bo jest to w twoim stylu? + +Nie ma sprawy, wpisz polecenie: + + $ git add -p + +Dla każdej zmiany, której dokonałaś Git pokaże ci pasaże z kodem, który uległ zmianom i spyta cię, czy mają zostać częścią następnego 'commit'. Odpowiedz po prostu "y" dla tak, albo "n" dla nie. Dysponujesz jeszcze innymi opcjami, na przykład dla odłożenie w czasie decyzji, wpisz "?", by dowiedzieć się więcej. + +Jeśli jesteś już zadowolony z wyniku, wpisz: + + $ git commit + +by dokonać wybranych zmian. Uważaj tylko, by nie skorzystać z opcji *-a*, ponieważ wtedy git dokona 'commit' zawierający wszystkie zmiany. + +A co, jeśli pracowałaś nad wieloma danymi w wielu różnych miejscach? Sprawdzenie każdej danej z osobna jest zarówno rustrujące jak i męczące. W takim wypadku skorzystaj z *git add -i*, obsługa tego polecenia może nie jest zbyt łatwa, za to jednak bardzo elastyczna. Kilkoma naciśnięciami klawiszy możesz wiele zmienionych plików dodać ('stage') albo usunąć z 'commit' ('unstage'), jak również sprawdzić, czy dodać zmiany dla poszczególnych plików. Alternaywnie możesz skorzystać z *git commit \--interactive*, polecenie to wykona automatycznie 'commit' gdy skończysz. + +=== Index: rusztowanie Gita === + +Do tej pory staraliśmy się omijać sławny 'indeks', jednak przyszedł czas się nim zająć, aby móc wyjaśnić wszystko to co poznaliśmy do tej pory. Index jest tymczasową przechowalnią. Git rzadko wymienia dane bezpośrednio między twoim projektem a swoją historią wersji. Raczej zapisuje on dane najpierw w indeksie, dopiero po tym kopiuje dane z indeksu na ich właściwe miejsce przeznaczenia. + +Na przykład polecenie *commit -a* jest właściwie procesem dwustopniowym. Pierwszy krok to stworzenie obrazu bieżącego statusu każdego monitorowanego pliku do indeksu. Drugim krokiem jest trwałe zapamiętanie obrazu do indeksu. Wykonanie 'commit' bez opcji *-a* wykona jedynie drugi wspomniany krok i ma jedynie sens, jeśli poprzednio wykonano komendę, która dokonała odpowiednich zmian w indeksie, na przykład *git add*. + +Normalnie możemy ignorować indeks i udawać, że czytamy i zapisujemy bezpośrednio z historii. W tym wypadku chcemy posiadać jednak większą kontrolę, więc manipulujemy indeks. Tworzymy obraz niektórych, jednak nie wszystkich zmian w indeksie i później zapamiętujemy trwale starannie dobrany obraz. + +=== Nie trać głowy! === + +Identyfikator 'HEAD' zachowuje się jak kursor, który zwykle wskazuje na najmłodszy 'commit' i z każdym nowym 'commit' zostaje przesunięty do przodu. Niektóre komendy Gita pozwolą ci nim manipulować. Na przyklad: + + $ git reset HEAD~3 + +przesunie identyfikator 'HEAD' o 3 'commits' spowrotem. Spowoduje to, że wszystkie następne komendy Gita będą reagować, jakby tych trzech ostatnich 'commits' wogóle nie było, podczas gdy twoje dane zostaną w przyszłości. Na stronach pomocy Gita znajdziesz więcej takich zastosowań. + +Ale jak teraz wrócić znów do przyszłości? Poprzednie 'commits' nic nie wiedzą o jej istnieniu. + +Jeśli posiadasz hash SHA1 orginalnego 'HEAD', wtedy możesz wrócić komendą: + + $ git reset 1b6d + +Wyobraź jednak sobie, że nigdy go nie notowałaś? Nie ma sprawy: Przy wykonywaniu takich poleceń Git archiwizuje orginalny HEAD jako indentyfikator o nazwie ORIG_HEAD a ty możesz bezproblemowo wrócić używając: + + $ git reset ORIG_HEAD + +=== Łowcy głów === + +Może się zdarzyć, że ORIG_HEAD nie wystarczy. Może właśnie spostrzegłaś, iż dokonałaś kapitalnego błędu i musisz wrócić się do bardzo starego 'commit' w zapomnianym 'branch'. + +Standardowo Git zapamiętuje 'commit' przez przynajmniej 2 tygodnie, nawet jeśli poleciłaś zniszczyć 'branch' w którym istniał. Problemem staje się tutaj odnalezienie odpowieniej sumy kontrolnej SHA1. Możesz po kolei testować wszystkie hashe SHA1 w `.git/objects` i w ten sposób próbować odnaleźć szukany 'commit'. Istnieje jednak na to dużo prostszy sposób. + +Git zapamiętuje każdy obliczony hash SHA1 dla odpowiednich 'commit' w `.git/logs`. Podkatalog `refs` zawieza przebieg wszystkich aktywności we wszystkich 'branches', podczas gdy plik `HEAD` wszystkie klucze SHA1 które kiedykolwiek posiadał. Ostatnie możemy zastosować do odnalezienia sum kontrolnych SHA1 tych 'commits' które znajdowały się w nieuważnie usuniętym 'branch'. + +Polecenie 'reflog' daje nam do dyspozycji przyjazny interfejs do tych właśnie logów. Wypróbuj polecenie: + + $ git reflog + +Zamiast kopiować i wklejać klucze z 'reflog', możesz: + + $ git checkout "@{10 minutes ago}" + +Albo przywołaj 5 z ostatnio oddwiedzanych 'commits' za pomocą: + + $ git checkout "@{5}" + +Jeśli chciałbyś pogłębić wiedze na ten temat przeczytaj sekcję ``Specifying Revisions`` w *git help rev-parse*. + +Byś może zechcesz zmienić okres karencji dla przeznaczonych na stracenie 'commits'. Na przyklad: + + $ git config gc.pruneexpire "30 days" + +znaczy, że skasowany 'commit' zostanie nieuchronnie utracony dopiero po 30 dniach od wykonania polecenia *git gc*. + +Jeśli chcałbyś zapobiec automatyycznemu wykonywaniu *git gc*: + + $ git config gc.auto 0 + +wtedy 'commits' będą tylko wtedy usuwane, gdy ręcznie wykonasz polecenie *git gc*. + +=== Budować na bazie Gita === + +W prawdziwym unixowym świecie sama konstrukcja Gita pozwala na wykorzystanie go jako funkcji niskiego poziomu przez inne aplikacje, jak na przykład interfejsy graficzne i aplikacje internetowe, alternatywne narzędzia konsoli, narzędzia patchujące, narzędzia pomocne w importowaniu i konwertowaniu i tak dalej. Nawek same polecenia Git są czasami malutkimi skryptami, jak krasnoludki na ramieniu olbrzyma. Przykładając trochę ręki możesz adoptować Git do twoich własnych potrzeb. + +Prostą sztuczką może być korzystanie z zintegrowanej w Git funkcji aliasu, by skrócić najczęściej stosowane polecenia: + + $ git config --global alias.co checkout + $ git config --global --get-regexp alias # wyświetli aktualne aliasy + alias.co checkout + $ git co foo # to samo co 'git checkout foo' + +Czymś troszeczkę innym będzie zapis nazwy aktualnego 'branch' w prompcie lub jako nazwy okna. Polecenie: + + $ git symbolic-ref HEAD + +pokaże nazwę aktualnego 'branch'. W praktyce chciałbyś raczej usunąć "refs/heads/" i ignorować błędy: + + $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- + +Podkatakog +contrib+ jest wielkim znaleziskiem narzędzi zbudowanych dla Gita. Z czasem niektóre z nich mogą uzyskać status oficjalnych poleceń. W dystrybucjach Debiana i Ubuntu znajdziemy ten katalog pod +/usr/share/doc/git-core/contrib+. + +Ulubionym przedstawicielem jest +workdir/git-new-workdir+. Poprzez sprytne przelinkowania skrypt ten tworzy nowy katalog roboczy, który dzieli swoją historię wersji z oryginalnym repozytorium: + + $ git-new-workdir istniejacy/repo nowy/katalog + +Ten nowy katalog i znajdujące się w nim pliki można sobie wyobrazić jako klon, z tą różnicą, że ze względu na wspólną niepodzieloną historie obje wersje pozostaną zsynchronizowane. Synchronizacja za pomocą 'merge', 'push', czy 'pull' nie będzie konieczna. + +=== Śmiałe wyczyny === + +Obecnie Git dość dobrze chroni użytkownika przed przypadkowym zniszczeniem danych. Ale, jeśli wiemy co robić, możemy obejść środki ochrony najczęściej stosowanych poleceń. + +*Checkout*: nie wersjonowane zmiany doprowadzą do niepowodzenia polecenia 'checkout'. Aby mimo tego zniszczyć zmiany i przywołać istniejący 'commit', możemy skorzystać z opcji 'force': + + $ git checkout -f HEAD^ + +Jeśli poleceniu 'checkout' podamy inną ścieżkę, środki ochrony nie znajdą zastosowania. Podana ścieżka zostanie bez pytania zastąpiona. Bądź ostrożny stosując 'checkout' w ten sposób. + +*reset*: reset odmówi pracy, jeśli znajdzie niewersjonowane zmiany. By zmusić go do tego, możesz użyć: + + $ git reset --hard 1b6d + +*Branch*: Skasowanie 'branches' też się nie powiedzie, jeśli mogłyby przez to zostać utracone zmiany. By wymusić skasowanie, podaj: + + $ git branch -D martwy_branch # zamiast -d + +Również nie uda się próba przesunięcia 'branch' poleceniem 'move', jeśliby miałoby to oznaczać utratę danych. By wymusić przesunięcie, podaj: + + $ git branch -M źródło cel # zamiast -m + +Inaczej niż w przypadku 'checkout' i 'reset', te oba polecenia przesuną zniszczenie danych. Zmiany te zostaną zapisane w podkatalogu .git i mogą znów zostać przywrócone, jeśli znajdziemy odpowiedni hash SHA1 w `.git/logs` (zobacz "Łowcy głów" powyżej). Standardowo dane te pozostają jeszcze przez 2 tygodnie. + +*clean*: Różnorakie polecenia Git nie chcą zadziałać, ponieważ podejżewają konflikty z niewersjonowanymi danymi. Jeśli jesteś pewny, że wszystkie niezwersjonowane pliki i katalogi są zbędne, skasujesz je bezlitośnie poleceniem: + + $ git clean -f -d + +Następnym razem te uciążliwe polecenia zaczną znów być posłuszne. + +=== Zapobiegaj złym 'commits' === + +Głupie błędy zaśmiecają moje repozytoria. Najbardziej fatalny jest brak plików z powodu zapomnianych *git add*. Mniejszymi usterkami mogą być spacje na końcu linii i nierozwiązane konflikty poleceń 'merge': mimo iż nie są groźne, życzyłbym sobie, by nigdy nie wystąpiły publicznie. + +Gdybym tylko zabezpieczył się wcześniej, stosując prosty _hook_, który alarmowałby mnie przy takich problemach. + + $ cd .git/hooks + $ cp pre-commit.sample pre-commit # Starsze wersje Gita wymagają jeszcze: chmod +x pre-commit + +I już 'commit' przerywa, jeśli odkryje niepotrzebne spacje na końcu linii albo nierozwiązane konflikty 'merge'. + +Na początku *pre-commit* tego 'hook' umieściłbym dla ochrony przed rozdrobnieniem: + + if git ls-files -o | grep '\.txt$'; then + echo FAIL! Untracked .txt files. + exit 1 + fi + +Wiele operacji Gita pozwala na używanie 'hooks'; zobacz też: *git help hooks*. We wcześniejszym rozdziale "Git poprzez http" przytoczyliśmy przykład 'hook' dla *post-update*, który wykonywany jest zawsze, jeśli znacznik 'HEAD' zostaje przesunięty. Ten przykładowy skrypt 'post-update' aktualizuje dane, które potrzebne są do komunikacji poprzez 'Git-agnostic transports', jak na przykład HTTP. diff --git a/pl/history.txt b/pl/history.txt dissimilarity index 89% index dfe9d69..bdbe2fd 100644 --- a/pl/history.txt +++ b/pl/history.txt @@ -1,260 +1,195 @@ -== Lessons of History == - -A consequence of Git's distributed nature is that history can be edited -easily. But if you tamper with the past, take care: only rewrite that part of -history which you alone possess. Just as nations forever argue over who -committed what atrocity, if someone else has a clone whose version of history -differs to yours, you will have trouble reconciling when your trees interact. - -Some developers strongly feel history should be immutable, warts and all. -Others feel trees should be made presentable before they are unleashed in -public. Git accommodates both viewpoints. Like cloning, branching, and merging, -rewriting history is simply another power Git gives you. It is up to you -to use it wisely. - -=== I Stand Corrected === - -Did you just commit, but wish you had typed a different message? Then run: - - $ git commit --amend - -to change the last message. Realized you forgot to add a file? Run *git add* to -add it, and then run the above command. - -Want to include a few more edits in that last commit? Then make those edits and run: - - $ git commit --amend -a - -=== ... And Then Some === - -Suppose the previous problem is ten times worse. After a lengthy session you've -made a bunch of commits. But you're not quite happy with the way they're -organized, and some of those commit messages could use rewording. Then type: - - $ git rebase -i HEAD~10 - -and the last 10 commits will appear in your favourite $EDITOR. A sample excerpt: - - pick 5c6eb73 Added repo.or.cz link - pick a311a64 Reordered analogies in "Work How You Want" - pick 100834f Added push target to Makefile - -Older commits precede newer commits in this list, unlike the `log` command. -Here, 5c6eb73 is the oldest commit, and 100834f is the newest. Then: - -- Remove commits by deleting lines. Like the revert command, but off the - record: it will be as if the commit never existed. -- Reorder commits by reordering lines. -- Replace `pick` with: - * `edit` to mark a commit for amending. - * `reword` to change the log message. - * `squash` to merge a commit with the previous one. - * `fixup` to merge a commit with the previous one and discard the log message. - -For example, we might replace the second `pick` with `squash`: - - pick 5c6eb73 Added repo.or.cz link - squash a311a64 Reordered analogies in "Work How You Want" - pick 100834f Added push target to Makefile - -After we save and quit, Git merges a311a64 into 5c6eb73. Thus *squash* merges -into the next commit up: think ``squash up''. - -Git then combines their log messages and presents them for editing. The -command *fixup* skips this step; the squashed log message is simply discarded. - -If you marked a commit with *edit*, Git returns you to the past, to the oldest -such commit. You can amend the old commit as described in the previous section, -and even create new commits that belong here. Once you're pleased with the -``retcon'', go forward in time by running: - - $ git rebase --continue - -Git replays commits until the next *edit*, or to the present if none remain. - -You can also abandon the rebase with: - - $ git rebase --abort - -So commit early and commit often: you can tidy up later with rebase. - -=== Local Changes Last === - -You're working on an active project. You make some local commits over time, and -then you sync with the official tree with a merge. This cycle repeats itself a few times before you're ready to push to the central tree. - -But now the history in your local Git clone is a messy jumble of your changes and the official changes. You'd prefer to see all your changes in one contiguous section, and after all the official changes. - -This is a job for *git rebase* as described above. In many cases you can use -the *--onto* flag and avoid interaction. - -Also see *git help rebase* for detailed examples of this amazing command. You can split commits. You can even rearrange branches of a tree. - -Take care: rebase is a powerful command. For complicated rebases, first make a -backup with *git clone*. - -=== Rewriting History === - -Occasionally, you need the source control equivalent of airbrushing people out -of official photos, erasing them from history in a Stalinesque fashion. For -example, suppose we intend to release a project, but it involves a file that -should be kept private for some reason. Perhaps I left my credit card number in -a text file and accidentally added it to the project. Deleting the file is -insufficient, for the file can be accessed from older commits. We must remove -the file from all commits: - - $ git filter-branch --tree-filter 'rm top/secret/file' HEAD - -See *git help filter-branch*, which discusses this example and gives a faster -method. In general, *filter-branch* lets you alter large sections of history -with a single command. - -Afterwards, the +.git/refs/original+ directory describes the state of affairs before the operation. Check the filter-branch command did what you wanted, then delete this directory if you wish to run more filter-branch commands. - -Lastly, replace clones of your project with your revised version if you want to interact with them later. - -=== Making History === - -[[makinghistory]] -Want to migrate a project to Git? If it's managed with one of the more well-known systems, then chances are someone has already written a script to export the whole history to Git. - -Otherwise, look up *git fast-import*, which reads text input in a specific -format to create Git history from scratch. Typically a script using this -command is hastily cobbled together and run once, migrating the project in a -single shot. - -As an example, paste the following listing into temporary file, such as `/tmp/history`: ----------------------------------- -commit refs/heads/master -committer Alice Thu, 01 Jan 1970 00:00:00 +0000 -data < - -int main() { - printf("Hello, world!\n"); - return 0; -} -EOT - - -commit refs/heads/master -committer Bob Tue, 14 Mar 2000 01:59:26 -0800 -data < - -int main() { - write(1, "Hello, world!\n", 14); - return 0; -} -EOT - ----------------------------------- - -Then create a Git repository from this temporary file by typing: - - $ mkdir project; cd project; git init - $ git fast-import --date-format=rfc2822 < /tmp/history - -You can checkout the latest version of the project with: - - $ git checkout master . - -The *git fast-export* command converts any repository to the -*git fast-import* format, whose output you can study for writing exporters, -and also to transport repositories in a human-readable format. Indeed, -these commands can send repositories of text files over text-only channels. - -=== Where Did It All Go Wrong? === - -You've just discovered a broken feature in your program which you know for sure was working a few months ago. Argh! Where did this bug come from? If only you had been testing the feature as you developed. - -It's too late for that now. However, provided you've been committing often, Git -can pinpoint the problem: - - $ git bisect start - $ git bisect bad HEAD - $ git bisect good 1b6d - -Git checks out a state halfway in between. Test the feature, and if it's still broken: - - $ git bisect bad - -If not, replace "bad" with "good". Git again transports you to a state halfway -between the known good and bad versions, narrowing down the possibilities. -After a few iterations, this binary search will lead you to the commit that -caused the trouble. Once you've finished your investigation, return to your -original state by typing: - - $ git bisect reset - -Instead of testing every change by hand, automate the search by running: - - $ git bisect run my_script - -Git uses the return value of the given command, typically a one-off script, to -decide whether a change is good or bad: the command should exit with code 0 -when good, 125 when the change should be skipped, and anything else between 1 -and 127 if it is bad. A negative return value aborts the bisect. - -You can do much more: the help page explains how to visualize bisects, examine -or replay the bisect log, and eliminate known innocent changes for a speedier -search. - -=== Who Made It All Go Wrong? === - -Like many other version control systems, Git has a blame command: - - $ git blame bug.c - -which annotates every line in the given file showing who last changed it, and when. Unlike many other version control systems, this operation works offline, reading only from local disk. - -=== Personal Experience === - -In a centralized version control system, history modification is a difficult -operation, and only available to administrators. Cloning, branching, and -merging are impossible without network communication. So are basic operations -such as browsing history, or committing a change. In some systems, users -require network connectivity just to view their own changes or open a file for -editing. - -Centralized systems preclude working offline, and need more expensive network -infrastructure, especially as the number of developers grows. Most -importantly, all operations are slower to some degree, usually to the point -where users shun advanced commands unless absolutely necessary. In extreme -cases this is true of even the most basic commands. When users must run slow -commands, productivity suffers because of an interrupted work flow. - -I experienced these phenomena first-hand. Git was the first version control -system I used. I quickly grew accustomed to it, taking many features for -granted. I simply assumed other systems were similar: choosing a version -control system ought to be no different from choosing a text editor or web -browser. - -I was shocked when later forced to use a centralized system. A flaky internet -connection matters little with Git, but makes development unbearable when it -needs to be as reliable as local disk. Additionally, I found myself conditioned -to avoid certain commands because of the latencies involved, which ultimately -prevented me from following my desired work flow. - -When I had to run a slow command, the interruption to my train of thought -dealt a disproportionate amount of damage. While waiting for server -communication to complete, I'd do something else to pass the time, such as -check email or write documentation. By the time I returned to the original -task, the command had finished long ago, and I would waste more time trying to -remember what I was doing. Humans are bad at context switching. - -There was also an interesting tragedy-of-the-commons effect: anticipating -network congestion, individuals would consume more bandwidth than necessary on -various operations in an attempt to reduce future delays. The combined efforts -intensified congestion, encouraging individuals to consume even more bandwidth -next time to avoid even longer delays. +== Lekcja historii == + +Jedną z charakterystycznych cech rozproszonej natury Git jest to, że jego kronika historii może być łatwo edytowana. Ale jeśli masz zamiar manipulować przeszłością, bądź ostrożny: zmieniaj tylko tą część historii, którą wyłącznie jedynie ty sam posiadasz. Tak samo jak Narody ciągle dyskutują, który jakie popełnił okrucieństwa, popadniesz w kłopoty przy synchronizacji, jeśli ktoś inny posiada klon z różniącą się historią i jeśli te odgałęzienia mają się wymieniać. + +Niektórzy programiści zarzekają się w kwestii nienaruszalności historii - ze wszystkimi jej błędami i niedociągnięciami. Inni uważają, że odgałęzienia powinny dobrze się prezentować nim zostaną przedstawione publicznie. Git jest wyrozumiały dla obydwu stron. Tak samo jak 'clone', 'branch', czy 'merge', możliwość zmian kroniki historii to tylko kolejna mocna strona Gita. Stosowanie lub nie, tej możliwości zależy wyłącznie od ciebie. + +=== Muszę się skorygować === + +Właśnie wykonałaś 'commit', ale chętnie chciałbyś podać inny opis? Wpisujesz: + + $ git commit --amend + +by zmienić ostatni opis. Zauważasz jednak, że zapomniałaś dodać jakiegoś pliku? Wykonaj *git add*, by go dodać a następnie poprzednią instrukcje. + +Chcesz wprowadzić jeszcze inne zmiany do ostatniego 'commit'? Wykonaj je i wpisz: + +$ git commit --amend -a + +=== ... i jeszcze coś === + +Załóżmy, że poprzedni problem będzie 10 razy gorszy. Po dłuższej sesji zrobiłaś całą masę 'commits'. Nie jesteś jednak szczęśliwy z takiego zorganizowania, a niektóre z 'commits' mogłyby być inaczej sformułowane. Wpisujesz: + + $ git rebase -i HEAD~10 + +a ostatnie 10 'commits' pojawią się w preferowanym przez ciebie edytorze. Przykładowy wyciąg: + + pick 5c6eb73 Added repo.or.cz link + pick a311a64 Reordered analogies in "Work How You Want" + pick 100834f Added push target to Makefile + +Starsze 'commits' poprzedzają młodsze, inaczej niż w poleceniu `log` +Tutaj 5c6eb73 jest najstarszym 'commit', a 100834f najnowszym. By to zmienić: + +- Usuń 'commits' poprzez skasowanie linii. Podobnie jak polecenie 'revert', będzie to jednak wyglądało jakby wybrane 'commit' nigdy nie istniały. +- Przeorganizuj 'commits' przesuwając linie. +- Zamień `pick` na: + * `edit` by zaznaczyć 'commit' do 'amend'. + * `reword`, by zmienić opisy logu. + * `squash` by połączyć 'commit' z poprzednim ('merge'). + * `fixup` by połączyć 'commit' z poprzednim ('merge') i usunąć zapisy z logu. + +Na przykład chcemy zastąpić drugi `pick` na `squash`: + +Zapamiętaj i zakończ. Jeśli zaznaczyłaś jakiś 'commit' do 'edit', wpisz: + + $ git commit --amend + + pick 5c6eb73 Added repo.or.cz link + squash a311a64 Reordered analogies in "Work How You Want" + pick 100834f Added push target to Makefile + +Po zapamiętaniu i wyjściu Git połączy a311a64 z 5c6eb73. +Thus *squash* merges +into the next commit up: think ``squash up''. + +Git połączy wiadomości logów i zaprezentuje je do edycji. Polecenie *fixup* pominie ten krok, wciśnięte logi zostaną pominięte. + +Jeśli zaznaczyłeś 'commit' opcją *edit*, Git przeniesie cię do najstarszego takiego 'commit'. Możesz użyć 'amend', jak opisane w poprzednim rozdziale, i utworzyć nowy 'commit' mający się tu znaleźć. Gdy już będziesz zadowolony z ``retcon'', przenieś się na przód w czasie: + + $ git rebase --continue + +Git powtarza 'commits' aż do następnego *edit* albo na przyszłość, jeśli żadne nie stoją na prożu. + +Możesz równierz zrezygnować z 'rebase': + + $ git rebase --abort + +A więc, stosuj polecenie 'commit' wcześnie i często: możesz później zawsze posprzątać za pomocą 'rebase'. + +=== Lokalne zmiany na koniec === + +Pracujesz nad aktywnym projektem. Z biegiem czasu nagromadziło się wiele 'commits' i wtedy chcesz zsynchronizować za pomocą 'merge' z oficjalną gałęzią. Ten cykl powtarza się kilka razy zanim jesteś gotowy na 'push' do centralnego drzewa. + +Teraz jednak historia w twoim lokalnym klonie jest chaotycznym pomieszaniem twoich zmian i zmian z oficjalnego drzewa. Chciałbyś raczej widzieć twoje zmiany uporządkowane chronologicznie w jednej sekcji i za oficjalnymi zmianami. + +To zadanie dla *git rebase*, jak opisano powyżej. W wielu przypadkach możesz skorzystać z przełącznika *--onto* by zapobiec interakcji. + +Przeczytaj też *git help rebase* dla zapoznania sie z obszernymi przykładami tej zadziwiającej funkcji. Możesz również podzielić 'commits'. Możesz nawet przeorganizować 'branches' w repozytorium. + +Bądź ostrożny korzystając z 'rebase', to bardzo mocne polecenie. Zanim dokonasz skomplikowanych 'rebase', zrób backup za pomocą *git clone* + +=== Przepisanie historii === + +Czasami potrzebny ci rodzaj systemu kontroli porównywalnego do wyretuszowania osób z oficjalnego zdjęcia, by w stalinowski sposób wymazać je z historii. Wyobraź sobie, że chcesz opublikować projekt, jednak zawiera on pewny plik, który z jakiegoś ważnego powodu musi pozostać utajniony. Być może zapisałem numer karty kredytowej w danej tekstowej i nieumyślnie dodałem do projektu? Skasowanie tej danej nie ma sensu, ponieważ poprzez starsze 'commits' można nadal ją przywołać. Musimy ten plik usunąć ze wszystkich 'commits': + + $ git filter-branch --tree-filter 'rm bardzo/tajny/plik' HEAD + +Sprawdź *git help filter-branch*, gdzie przykład ten został wytłumaczony i przytoczona została jeszcze szybsza metoda. Ogólnie poprzez *filter-branch* da się dokonać zmian w dużych zakresach historii poprzez tylko jedno polecenie. + +Po tej operacji katalog +.git/refs/original+ opisuje stan przed jej wykonaniem. Sprawdź czy 'filter-branch' zrobił to, co od niego oczekiwałaś, następnie skasuj ten katalog zanim wykonasz następne polecenia 'filter-branch'. + +Wreszcie zamień wszystkie klony twojego projektu na zaktualizowaną wersję, jeśli masz zamiar prowadzić z nimi wymianę. + +=== Tworzenie historii === + +[[makinghistory]] +Masz zamiar przenieść projekt do Gita? Jeśli twój projekt był dotychczas zarządzany jednym z bardziej znanych systemów, to istnieje duże prawdopodobieństwo, że ktoś napisał już odpowiedni skrypt, który umożliwi ci eksportowanie do Gita całej historii. + +W innym razie przyjrzyj się funkcji *git fast-import*, która wczytuje tekst w specjalnym formacie by następnie odtworzyć całą historię od początku. Często taki skrypt pisany jest pośpiesznie i służy do jednorazowego wykorzystania, aby tylko w jednym przebiegu udała się migracja projektu. + +Utwórz na przykład z następującej listy tymczasowy plik, na przykład jako: `/tmp/history`: +---------------------------------- +commit refs/heads/master +committer Alice Thu, 01 Jan 1970 00:00:00 +0000 +data < + +int main() { + printf("Hello, world!\n"); + return 0; +} +EOT + + +commit refs/heads/master +committer Bob Tue, 14 Mar 2000 01:59:26 -0800 +data < + +int main() { + write(1, "Hello, world!\n", 14); + return 0; +} +EOT + +---------------------------------- + +Następnie utwórz repozytorium Git z tymczasowego pliku poprzez wpisanie: + + $ mkdir project; cd project; git init + $ git fast-import --date-format=rfc2822 < /tmp/history + +Aktualną wersję projektu możesz przywołać poprzez: + + $ git checkout master + +Polecenie *git fast-export* konwertuje każde repozytorium do formatu *git fast-import*, możesz przestudiować komunikaty tego polecenia, jeśli masz zamiar napisać programy eksportujące a oprócz tego, by przekazywać repozytoria jako czytelne dla ludzi zwykłe pliki tekstowe. To polecenie potrafi przekazywać repozytoria za pomocą zwykłego pliku tekstowego. + +=== Gdzie wszystko się zepsuło? === + +Właśnie znalazłaś w swoim programie funkcję, która już nie chce działać, a jesteś pewna, że czyniła to jeszcze kilka miesięcy temu. Ach! Skąd wziął się ten błąd? A gdybym tylko lepiej przetestowała ją wcześniej, zanim weszła do wersji produkcyjnej. + +Na to jest już za późno. Jakby nie było, pod warunkiem, że często używałaś 'commit', Git może ci zdradzić gdzie szukać problemu. + + $ git bisect start + $ git bisect bad HEAD + $ git bisect good 1b6d + +Git przywoła stan, który leży dokładnie pośrodku. Przetestuj funkcję, a jeśli ciągle jeszcze nie działa: + + $ git bisect bad + +Jeśli nie, zamień "bad" na "good". Git przeniesie cię znowu do stanu dokładnie pomiędzy znanymi wersjami "good" i "bad", redukując w ten sposób możliwości. Po kilku iteracjach doprowadzą cię te poszukiwania do 'commit', który jest odpowiedzialny za kłopoty. Po skończeniu dochodzenia przejdź do oryginalnego stanu: + + $ git bisect reset + +Zamiast sprawdzania zmian ręcznie, możesz zautomatyzować poszukiwania za pomocą skryptu: + + $ git bisect run mój_skrypt + +Git korzysta tutaj z wartości zwróconej przez skrypt, by ocenić czy zmiana jest dobra ('good'), czy zła ('bad'): Skrypt powinien zwracać 0 dla 'good', 128, jeśli zmiana powinna być pominięta, i coś pomiędzy 1 - 127 dla 'bad'. Jeśli wartość zwrócona jest ujemna, program 'bisect' przerywa pracę. + +Możesz robić jeszcze dużo innych rzeczy: w pomocy znajdziesz opis w jaki sposób wizualizować działania 'bisect', sprawdzić czy powtórzyć log bisect, wyeliminować nieistotne zmiany dla zwiększenia prędkości poszukiwań. + +=== Kto ponosi odpowiedzialność? === + +Jak i wiele innych systemów kontroli wersji również i Git posiada polecenie 'blame': + + $ git blame bug.c + +które komentuje każdą linię podanego pliku, by pokazać kto ją ostatnio zmieniał i kiedy. W przeciwieństwie do wielu innych systemów, funkcja ta działa offline, czytając tylko z lokalnego dysku. + +=== Osobiste doświadczenia === + +W scentralizowanym systemie kontroli wersji praca nad kroniką historii jest skomplikowanym zadaniem i zarezerwowanym głównie dla administratorów. Polecenia 'clone', 'branch' czy 'merge' nie są możliwe bez podłączenia do sieci. Również takie podstawowe funkcje, jak przeszukanie historii czy 'commit' jakiejś zmiany. W niektórych systemach użytkownik potrzebuje działającej sieci nawet by zobaczyć dokonane przez siebie zmiany, albo by w ogóle otworzyć plik do edycji. + +Scentralizowane systemy wykluczają pracę offline i wymagają drogiej infrastruktury sieciowej, w szczególności gdy wzrasta liczba programistów. Najgorsze jednak, iż z czasem wszystkie operacje stają się wolniejsze, z reguły do osiągnięcia punktu, gdzie użytkownicy unikają zaawansowanych poleceń, aż staną się one absolutnie konieczne. W ekstremalnych przypadkach dotyczy to również poleceń podstawowych. Jeśli użytkownicy są zmuszeni do wykonywania powolnych poleceń, produktywność spada, ponieważ ciągle przerywany zostaje tok pracy. + +Dowiedziałem się o tym fenomenie na sobie samym. Git był pierwszym systemem kontroli wersji którego używałem. Szybko dorosłem do tej aplikacji i przyjąłem wiele funkcji za oczywiste. Wychodziłem też z założenia, że inne systemy są podobne: wybór systemu kontroli wersji nie powinien zbyt bardzo odbiegać od wyboru edytora tekstu, czy przeglądarki internetowej. + +Byłem zszokowany, gdy musiałem później korzystać ze scentralizowanego systemu. Niesolidne połączenie internetowe ma niezbyt duży wpływ na Gita, praca staje się jednak prawie nie możliwa, gdy wymagana jest niezawodność porównywalny z lokalnym dyskiem. Poza tym sam łapałem się na tym, że unikałem pewnych poleceń i związanym z nimi czasem oczekiwania, w sumie wszystko to wpływało mocno na wypracowany przeze mnie system pracy. + +Gdy musiałem wykonywać powolne polecenia, z powodu ciągłego przerywanie toku myślenia, powodowałem nieporównywalne szkody dla całego przebiegu pracy. Podczas oczekiwania na zakończenie komunikacji pomiędzy serwerami dla przeczekania zaczynałem robić coś innego, na przykład czytałem maile albo pisałem dokumentację. Gdy wracałem do poprzedniego zajęcia, po zakończeniu komunikacji, dawno straciłem wątek i traciłem czas, by znów przypomnieć sobie co właściwie miałem zamiar zrobić. Ludzie nie potrafią dobrze dostosować się do częstej zmiany kontekstu. + +Był też taki ciekawy efekt http://pl.wikipedia.org/wiki/Tragedia_wspólnego_pastwiska[tragedii wspólnego pastwiska]: przypominający przeciążenia w sieci - pojedyncze indywidua pochłaniają więcej pojemności sieci niż to konieczne, by uchronić się przed mogącymi ewentualnie wystąpić w przyszłości niedoborami. Suma tych starań pogarsza tylko przeciążenia, co motywuje jednostki do zużywania jeszcze większych zasobów, by ochronić się przed jeszcze dłuższymi czasami oczekiwania. diff --git a/pl/intro.txt b/pl/intro.txt dissimilarity index 99% index 477f80a..b18a344 100644 --- a/pl/intro.txt +++ b/pl/intro.txt @@ -1,59 +1,57 @@ -== Introduction == - -I'll use an analogy to introduce version control. See http://en.wikipedia.org/wiki/Revision_control[the Wikipedia entry on revision control] for a saner explanation. - -=== Work is Play === - -I've played computer games almost all my life. In contrast, I only started using version control systems as an adult. I suspect I'm not alone, and comparing the two may make these concepts easier to explain and understand. - -Think of editing your code, or document, as playing a game. Once you've made a lot of progress, you'd like to save. To do so, you click on the 'Save' button in your trusty editor. - -But this will overwrite the old version. It's like those old school games which only had one save slot: sure you could save, but you could never go back to an older state. Which was a shame, because your previous save might have been right at an exceptionally fun part of the game that you'd like to revisit one day. Or worse still, your current save is in an unwinnable state, and you have to start again. - -=== Version Control === - -When editing, you can 'Save As...' a different file, or copy the file somewhere first before saving if you want to savour old versions. You can compress them too to save space. This is a primitive and labour-intensive form of version control. Computer games improved on this long ago, many of them providing multiple automatically timestamped save slots. - -Let's make the problem slightly tougher. Say you have a bunch of files that go together, such as source code for a project, or files for a website. Now if you want to keep an old version you have to archive a whole directory. Keeping many versions around by hand is inconvenient, and quickly becomes expensive. - -With some computer games, a saved game really does consist of a directory full of files. These games hide this detail from the player and present a convenient interface to manage different versions of this directory. - -Version control systems are no different. They all have nice interfaces to manage a directory of stuff. You can save the state of the directory every so often, and you can load any one of the saved states later on. Unlike most computer games, they're usually smart about conserving space. Typically, only a few files change from version to version, and not by much. Storing the differences instead of entire new copies saves room. - -=== Distributed Control === - -Now imagine a very difficult computer game. So difficult to finish that many experienced gamers all over the world decide to team up and share their saved games to try to beat it. Speedruns are real-life examples: players specializing in different levels of the same game collaborate to produce amazing results. - -How would you set up a system so they can get at each other's saves easily? And upload new ones? - -In the old days, every project used centralized version control. A server somewhere held all the saved games. Nobody else did. Every player kept at most a few saved games on their machine. When a player wanted to make progress, they'd download the latest save from the main server, play a while, save and upload back to the server for everyone else to use. - -What if a player wanted to get an older saved game for some reason? Maybe the current saved game is in an unwinnable state because somebody forgot to pick up an object back in level three, and they want to find the latest saved game where the game can still be completed. Or maybe they want to compare two older saved games to see how much work a particular player did. - -There could be many reasons to want to see an older revision, but the outcome is the same. They have to ask the central server for that old saved game. The more saved games they want, the more they need to communicate. - -The new generation of version control systems, of which Git is a member, are known as distributed systems, and can be thought of as a generalization of centralized systems. When players download from the main server they get every saved game, not just the latest one. It's as if they're mirroring the central server. - -This initial cloning operation can be expensive, especially if there's a long history, but it pays off in the long run. One immediate benefit is that when an old save is desired for any reason, communication with the central server is unnecessary. - -=== A Silly Superstition === - -A popular misconception is that distributed systems are ill-suited for projects requiring an official central repository. Nothing could be further from the truth. Photographing someone does not cause their soul to be stolen. Similarly, cloning the master repository does not diminish its importance. - -A good first approximation is that anything a centralized version control system can do, a well-designed distributed system can do better. Network resources are simply costlier than local resources. While we shall later see there are drawbacks to a distributed approach, one is less likely to make erroneous comparisons with this rule of thumb. - -A small project may only need a fraction of the features offered by such a -system, but using systems that scale poorly for tiny projects is like using -Roman numerals for calculations involving small numbers. - -Moreover, your project may grow beyond your original expectations. Using Git from the outset is like carrying a Swiss army knife even though you mostly use it to open bottles. On the day you desperately need a screwdriver you'll be glad you have more than a plain bottle-opener. - -=== Merge Conflicts === - -For this topic, our computer game analogy becomes too thinly stretched. Instead, let us again consider editing a document. - -Suppose Alice inserts a line at the beginning of a file, and Bob appends one at the end of his copy. They both upload their changes. Most systems will automatically deduce a reasonable course of action: accept and merge their changes, so both Alice's and Bob's edits are applied. - -Now suppose both Alice and Bob have made distinct edits to the same line. Then it is impossible to proceed without human intervention. The second person to upload is informed of a _merge conflict_, and must choose one edit over another, or revise the line entirely. - -More complex situations can arise. Version control systems handle the simpler cases themselves, and leave the difficult cases for humans. Usually their behaviour is configurable. +== Wprowadzenie == + +By wprowadzić w zagadnienie kontroli wersji, posłużę się pewną analogią. Dla bardziej rozsądnego wyjaśnienia przeczytajcie http://pl.wikipedia.org/wiki/System_kontroli_wersji[Artykuł Wikipedii] na ten temat. + +=== Praca jest zabawą === + +Gram w gry komputerowe chyba już przez całe moje życie. W przeciwieństwie do tego, systemy kontroli wersji zacząłem stosować dopiero jako dorosły. Przypuszczam, że nie jestem tu odosobniony, a porównanie to pomoże mi w prosty sposób wytłumaczyć jego idee. + +Wyobraź sobie pracę nad twoim kodem albo edycję dokumentu jak granie na komputerze. Jeśli dobrze ci poszło, chcesz zabezpieczyć swoje osiągnięcia. W tym celu klikasz na 'zapisz' w wybranym edytorze. + +Niestety wymaże to poprzednio zapamiętaną wersję. To jak w grach starej szkoły, które posiadały pamięć na zapisanie tylko jednego stanu: oczywiście, mogłaś zapamiętać, ale już nigdy nie mogłaś powrócić do poprzednio zapisanej wersji. To była hańba, bo być może poprzednio zabezpieczony stan znajdował się w jakimś bardzo interesującym miejscu gry, do którego chętnie chciałbyś jeszcze kiedyś wrócić. Albo jeszcze gorzej, twój zabezpieczony stan utknął w niemożliwym do dokończenia gry miejscu i musisz zacząć wszystko od początku. + +=== Kontrola wersji === + +Podczas edytowania dokumentu, by uchronić starą wersję, możesz poprzez wybranie 'zapisz jako ...' zapisać twój dokument pod inną nazwą lub zapamiętać w innym miejscu. Poza tym możesz go jeszcze spakować, by zaoszczędzić miejsce na dysku. Jest to prymitywna i pracochłonna forma kontroli wersji. Gry komputerowe robią tak już od długiego czasu, wiele z nich posiada tak automatycznie utworzone punkty opatrzone sygnaturą czasu. + +Skomplikujmy teraz trochę cały ten problem. Powiedzmy, że posiadasz całą masę plików, które w jakiś sposób są ze sobą powiązane, na przykład kod źródłowy jakiegoś projektu lub pliki strony internetowej. Jeśli chcesz otrzymać starszą wersję musisz archiwizować cały katalog. Archiwizowanie w ten sposób wielu wersji jest pracochłonne i szybko może stać się kosztowne, zabierając niepotrzebnie miejsce na dysku. + +Niektóre gry komputerowe składały się rzeczywiście z jednego katalogu pełnego plików. Gry ukrywały szczegóły przed graczem i prezentowały wygodny interfejs, do zarządzania różnymi wersjami katalogu. + +Systemy kontroli wersji nie różnią się tutaj zbytnio. Wszystkie posiadają wygodne interfejsy, umożliwiającymi zarządzanie katalogami pełnymi plików. Możesz archiwizować stan katalogu tak często jak często zechcesz i później możesz do każdego z tych punktów powrócić. W przeciwieństwie jednak do gier, są one z reguły wszystkie zoptymalizowane pod kątem oszczędności pamięci. W większości przypadków tylko niewiele danych ulega zmianie pomiędzy dwoma wersjami, a same zmiany nie są zbyt obszerne. Oszczędność miejsca na dysku polega głównie na zapamiętywaniu jedynie różnic, a nie kopii całego katalogu. + +=== Kontrola rozproszona === + +Wyobraź sobie teraz bardzo trudną grę komputerową. Tak trudną, że wielu doświadczonych graczy na całym świecie postanawia o wspólnych siłach przejść grę, wymieniając się w tym celu swoimi wynikami. 'Speedruns' mogą posłużyć jako przykład z prawdziwego życia: gracze, którzy wyspecjalizowali się w różnych poziomach gry współpracują ze sobą dla uzyskania fascynujących wyników. + +W jaki sposób skonstruowałbyś taki system, który w prosty sposób byłby w stanie udostępnić osiągnięcia innych? I dodawał nowe? + +Kiedyś każdy projekt korzystał z własnego scentralizowanego systemu kontroli wersji. Jeden serwer zapamiętywał wszystkie gry, nikt inny. Każdy gracz posiadał jedynie kilka zapamiętanych na swoim komputerze gier. Jeśli jakiś gracz chciał popchać grę trochę do przodu, musiał najpierw zładować z serwera jej aktualny stan, trochę pograć, zapisać własny stan, a następnie załadować na serwer, by mógł go wykorzystać ktoś inny. + +A gdy jakiś gracz z jakiegoś powodu chce otrzymać jakiś starszy stan? Może aktualnie zapamiętany stan gry nie jest do przejścia, bo ktoś na trzecim poziomie zapomniał zabrać jakiś obiekt, no i teraz próbują znaleźć stan od którego startując gra znowu stanie się możliwa do przejścia. Albo chcą porównać dwa stany, by sprawdzić ile któryś gracz włożył pracy. + +Istnieje wiele powodów, dla których można chcieć zobaczyć straszą wersję, rezultat jednak jest zawsze taki sam. Za każdym razem trzeba ściągnąć wszystkie dane z serwera. Czym więcej gier zostało zapamiętanych, tym więcej wymaga to komunikacji. + +Nową generację systemów kontroli wersji, do których zalicza się również Git, nazywa się systemami rozproszonymi, mogą być one rozumiane jako uogólnienie systemów scentralizowanych. Jeśli gracze ładują teraz z serwera, otrzymują każdy zapisany stan, a nie tylko zapisany jako ostatni. Wygląda to jak tworzenie kopii lustrzanej serwera. + +Stworzenie pierwszego klonu może wydać się drogie, przede wszystkim, jeśli projekt posiada długą historię, ale na dłuższy okres to się opłaci. Jedną z bezpośrednich zalet jest to, że kiedykolwiek potrzebny będzie nam jakiś starszy stan, komunikacja z głównym serwerem będzie zbędna. + +=== Głupi przesąd === + +Szeroko rozpowszechnianym nieporozumieniem jest opinia, że rozproszony system nie nadaje się dla projektów wymagających oficjalnego centralnego repozytorium. Nic bardziej mylnego. Fotografując kogoś nie kradniemy od razu jego duszy. Tym samym klonowanie centralnego repozytorium nie umniejsza jego znaczenia. + +Jednym z pierwszych pozytywnych skutków jest to, iż wszystko co potrafi scentralizowany system kontroli wersji, dobrze skonstruowany system rozproszony potrafi lepiej. Zasoby sieciowe są po prostu droższe niż zasoby lokalne. Nawet jeśli w późniejszym czasie dostrzeżemy pewne niedociągnięcia systemów rozproszonych, można powyższe przyjąć jako ogólną zasadę, unikając niestosownych porównań. + +Mały projekt wykorzysta prawdopodobnie tylko ułamek możliwości systemu. Ale, by od razu z tego powodu korzystać z prostszego systemu, nie posiadającego możliwości późniejszej rozbudowy, to tak jak stosowanie rzymskich cyfr do przeprowadzania obliczeń na małych liczbach. + +Ponadto możliwe, że twój projekt przerośnie początkowe oczekiwania. Używanie Gita od samego początku, to jak noszenie ze sobą szwajcarskiego scyzoryka, nawet gdy najczęściej służy do otwierania butelek. Być może pewnego dnia będziesz pilnie potrzebowała użyć śrubokręt, ucieszysz się, że masz przy sobie coś więcej niż tylko zwykły otwieracz. + +=== Kolizje przy scalaniu === + +Do przedstawienia tego tematu wykorzystanie analogii do gier komputerowych byłoby naciągane. Wyobraźmy sobie znowu, że edytujemy dokument. + +Alicja dodaje linijkę na początku dokumentu, natomiast Bob linijkę na jego końcu. Obydwoje ładują swoje zmiany na serwer. Większość systemów automatycznie wybierze rozsądną drogę: zaakceptuje obie zmiany i połączy je ze sobą, tym samym obje poprawki wpłyną do dokumentu. + +Wyobraź sobie jednak, że Alicja i Bob dokonują zmian w tej samej linijce. W tym wypadku dalsza praca nie będzie możliwa bez ludzkiego udziału. Druga z osób, próbująca zładować dokument na serwer, zostanie poinformowana o wystąpieniu konfliktu podczas łączenia ('merge') i musi zadecydować, którą ze zmian przyjąć, ewentualnie ponownie zrewidować całą linijkę. + +Mogą wystąpić dużo bardziej skomplikowane sytuacje. Systemy kontroli wersji potrafią poradzić sobie z prostymi przypadkami a te trudniejsze pozostawiają ludziom. Zazwyczaj sposób ich zachowania można skonfigurować. diff --git a/pl/multiplayer.txt b/pl/multiplayer.txt dissimilarity index 98% index aafd2ec..3bab68e 100644 --- a/pl/multiplayer.txt +++ b/pl/multiplayer.txt @@ -1,233 +1,167 @@ -== Multiplayer Git == - -Initially I used Git on a private project where I was the sole developer. -Amongst the commands related to Git's distributed nature, I needed only *pull* -and *clone* so could I keep the same project in different places. - -Later I wanted to publish my code with Git, and include changes from -contributors. I had to learn how to manage projects with multiple developers -from all over the world. Fortunately, this is Git's forte, and arguably its -raison d'être. - -=== Who Am I? === - -Every commit has an author name and email, which is shown by *git log*. -By default, Git uses system settings to populate these fields. -To set them explicitly, type: - - $ git config --global user.name "John Doe" - $ git config --global user.email johndoe@example.com - -Omit the global flag to set these options only for the current repository. - -=== Git Over SSH, HTTP === - -Suppose you have SSH access to a web server, but Git is not installed. Though -less efficient than its native protocol, Git can communicate over HTTP. - -Download, compile and install Git in your account, and create a repository in -your web directory: - - $ GIT_DIR=proj.git git init - $ cd proj.git - $ git --bare update-server-info - $ cp hooks/post-update.sample hooks/post-update - -For older versions of Git, the copy command fails and you should run: - - $ chmod a+x hooks/post-update - -Now you can publish your latest edits via SSH from any clone: - - $ git push web.server:/path/to/proj.git master - -and anybody can get your project with: - - $ git clone http://web.server/proj.git - -=== Git Over Anything === - -Want to synchronize repositories without servers, or even a network connection? -Need to improvise during an emergency? We've seen <>. We could shuttle such files back and forth to transport git -repositories over any medium, but a more efficient tool is *git bundle*. - -The sender creates a 'bundle': - - $ git bundle create somefile HEAD - -then transports the bundle, +somefile+, to the other party somehow: email, -thumb drive, an *xxd* printout and an OCR scanner, reading bits over the phone, -smoke signals, etc. The receiver retrieves commits from the bundle by typing: - - $ git pull somefile - -The receiver can even do this from an empty repository. Despite its -size, +somefile+ contains the entire original git repository. - -In larger projects, eliminate waste by bundling only changes the other -repository lacks. For example, suppose the commit ``1b6d...'' is the most -recent commit shared by both parties: - - $ git bundle create somefile HEAD ^1b6d - -If done frequently, one could easily forget which commit was last sent. The -help page suggests using tags to solve this. Namely, after you send a bundle, -type: - - $ git tag -f lastbundle HEAD - -and create new refresher bundles with: - - $ git bundle create newbundle HEAD ^lastbundle - -=== Patches: The Global Currency === - -Patches are text representations of your changes that can be easily understood -by computers and humans alike. This gives them universal appeal. You can email a -patch to developers no matter what version control system they're using. As long -as your audience can read their email, they can see your edits. Similarly, on -your side, all you require is an email account: there's no need to setup an online Git repository. - -Recall from the first chapter: - - $ git diff 1b6d > my.patch - -outputs a patch which can be pasted into an email for discussion. In a Git -repository, type: - - $ git apply < my.patch - -to apply the patch. - -In more formal settings, when author names and perhaps signatures should be -recorded, generate the corresponding patches past a certain point by typing: - - $ git format-patch 1b6d - -The resulting files can be given to *git-send-email*, or sent by hand. You can also specify a range of commits: - - $ git format-patch 1b6d..HEAD^^ - -On the receiving end, save an email to a file, then type: - - $ git am < email.txt - -This applies the incoming patch and also creates a commit, including information such as the author. - -With a browser email client, you may need to click a button to see the email in its raw original form before saving the patch to a file. - -There are slight differences for mbox-based email clients, but if you use one -of these, you're probably the sort of person who can figure them out easily -without reading tutorials! - -=== Sorry, We've Moved === - -After cloning a repository, running *git push* or *git pull* will automatically -push to or pull from the original URL. How does Git do this? The secret lies in -config options created with the clone. Let's take a peek: - - $ git config --list - -The +remote.origin.url+ option controls the source URL; ``origin'' is a nickname -given to the source repository. As with the ``master'' branch convention, we may -change or delete this nickname but there is usually no reason for doing so. - -If the original repository moves, we can update the URL via: - - $ git config remote.origin.url git://new.url/proj.git - -The +branch.master.merge+ option specifies the default remote branch in -a *git pull*. During the initial clone, it is set to the current branch of the -source repository, so even if the HEAD of the source repository subsequently -moves to a different branch, a later pull will faithfully follow the -original branch. - -This option only applies to the repository we first cloned from, which is -recorded in the option +branch.master.remote+. If we pull in from other -repositories we must explicitly state which branch we want: - - $ git pull git://example.com/other.git master - -The above explains why some of our earlier push and pull examples had no -arguments. - -=== Remote Branches === - -When you clone a repository, you also clone all its branches. You may not have -noticed this because Git hides them away: you must ask for them specifically. -This prevents branches in the remote repository from interfering with -your branches, and also makes Git easier for beginners. - -List the remote branches with: - - $ git branch -r - -You should see something like: - - origin/HEAD - origin/master - origin/experimental - -These represent branches and the HEAD of the remote repository, and can be used -in regular Git commands. For example, suppose you have made many commits, and -wish to compare against the last fetched version. You could search through the -logs for the appropriate SHA1 hash, but it's much easier to type: - - $ git diff origin/HEAD - -Or you can see what the ``experimental'' branch has been up to: - - $ git log origin/experimental - -=== Multiple Remotes === - -Suppose two other developers are working on our project, and we want to -keep tabs on both. We can follow more than one repository at a time with: - - $ git remote add other git://example.com/some_repo.git - $ git pull other some_branch - -Now we have merged in a branch from the second repository, and we have -easy access to all branches of all repositories: - - $ git diff origin/experimental^ other/some_branch~5 - -But what if we just want to compare their changes without affecting our own -work? In other words, we want to examine their branches without having -their changes invade our working directory. Then rather than pull, run: - - $ git fetch # Fetch from origin, the default. - $ git fetch other # Fetch from the second programmer. - -This just fetches histories. Although the working directory remains untouched, -we can refer to any branch of any repository in a Git command because we now -possess a local copy. - -Recall that behind the scenes, a pull is simply a *fetch* then *merge*. -Usually we *pull* because we want to merge the latest commit after a fetch; -this situation is a notable exception. - -See *git help remote* for how to remove remote repositories, ignore certain -branches, and more. - -=== My Preferences === - -For my projects, I like contributors to prepare repositories from which I can -pull. Some Git hosting services let you host your own fork of a project with -the click of a button. - -After I fetch a tree, I run Git commands to navigate and examine the changes, -which ideally are well-organized and well-described. I merge my own changes, -and perhaps make further edits. Once satisfied, I push to the main repository. - -Though I infrequently receive contributions, I believe this approach scales -well. See -http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[this -blog post by Linus Torvalds]. - -Staying in the Git world is slightly more convenient than patch files, as it -saves me from converting them to Git commits. Furthermore, Git handles details -such as recording the author's name and email address, as well as the time and -date, and asks the author to describe their own change. +== Multiplayer Git == + +Na początku zastosowałem Git w prywatnym projekcie, gdzie byłem jedynym programistą. Z poleceń, w związku z rozproszoną naturą Gita, potrzebowałem jedynie komende *pull* i *clone*, dzięki czemu mogłem trzymać ten sam projekt w kilku miejscach. + +Później chciałem opublikować mój kod za pomocą Gita i dołączyć zmiany kolegów. Musiałem nauczyć się zarządzać projektami, nad którymi zaangażowani byli programiści z całego świata. Na szczęście jest to silną stroną Gita i chyba jego racją bytu. + +=== Kim jestem? === + +Każdy 'commit' otrzymuje nazwę i adres e-mail autora, które zostaną pokazane w *git log*. Standardowo Git korzysta z ustawień systemowych do wypełnienia tych pól. Aby wprowadzić te dane bezpośrednio, podaj: + + $ git config --global user.name "Jan Kowalski" + $ git config --global user.email jan.kowalski@example.com + +Jeśli opóścisz przełącznik '--global' zmiany zostaną zastosowane wyłącznie do aktualnego repozytorium. + +=== Git przez SSH, HTTP === + +Załóżmy, posiadasz dostęp SSH do serwera stron internetowych, gdzie jednak Git nie został zainstalowany. Nawet, jeśli jest to mniej efektywne jak rodzimy protokół 'GIT', Git potrafi komunikować się również przez HTTP. + +Zładuj Git, skompiluj i zainstaluj pod własnym kontem oraz utwórz repozytorium w twoim katalogu strony internetowej. + + $ GIT_DIR=proj.git git init + $ cd proj.git + $ git --bare update-server-info + $ cp hooks/post-update.sample hooks/post-update + +Przy starszych wersjach Gita samo polecenie 'cp' nie wystarczy, wtedy musisz jeszcze: + + $ chmod a+x hooks/post-update + +Od teraz możesz publikować aktualizacje z każdego klonu poprzez SSH. + + $ git push web.server:/sciezka/do/proj.git master + +i każdy może teraz sklonować twój projekt przez HTTP: + + $ git clone http://web.server/proj.git + +=== Git ponad wszystko === + +Chciałbyś synchronizować repozytoria bez pomocy serwera czy nawet bez użycia sieci komputerowej? Musisz improwizować w nagłym wypadku? Widzieliśmy, że poleceniami <>. W ten sposób możemy transportować tego typu pliki za pomocą dowolnego medium, jednak bardziej wydajnym narzędziem jest *git bundle*. + +Nadawca tworzy 'bundle': + + $ git bundle create plik HEAD + +i transportuje 'bundle' +plik+ do innych zaangażowanych: przez e-mail, pendrive, *xxd* hexdump i skaner OCR, kod morsea, przez telefon, znaki dymne, itd. Odbiorca wyciąga 'commits' z 'bundle' poprzez podanie: + + $ git pull plik + +Odbiorca może to zrobić z pustym repozytorium. Mimo swojej wielkości +plik+ zawiera kompletny oryginał repozytorium. + +W dużych projektach unikniesz śmieci danych, jeśli tylko zrobisz 'bundle' zmian brakujących w innych repozytoriach. Na przykład załóżmy, że 'commit' ``1b6d...'' jest najaktualniejszym, które posiadają obie partie: + + $ git bundle create plik HEAD ^1b6d + +Jeśli robi się to regularnie, łatwo można zapomnieć, który 'commit' został wysłany ostatnio. Strony pomocy zalecają stosowanie tagów, by rozwiązać ten problem. To znaczy, po wysłaniu 'bundle', podaj: + + $ git tag -f ostatni_bundle HEAD + +a nowy 'bundle' tworzymy następnie poprzez: + + $ git bundle create nowy_bundle HEAD ^ostatni_bundle + +=== Patches: globalny środek płatniczy === + +'Patches' to jawne zobrazowanie twoich zmian, które mogą być jednocześnie rozumiane przez komputer i człowieka. Dodaje im to uniwersalnej mocy przyciągania. Możesz wysłać patch prowadzącym projekt, niezależnie od tego, jakiego używają systemu kontroli wersji. Doputy twoi współpracownicy potrafią czytać swoje maile, mogą widzieć również twoje zmiany. Również i z twojej strony wszystko, czego ci potrzeba to funkcjonujące konto e-mailowe: nie istnieje konieczność zakładania repozytorium online. + +Przypomnij sobie pierwszy rozdział: + + $ git diff 1b6d > mój.patch + +produkuje 'patch', który można dołączyć do e-maila dla dalszej dyskusji. W repozytorium Git natomiast podajesz: + + $ git apply < mój.patch + +By zastosować patch. + +W bardziej oficjalnym środowisku, jeśli nazwiska autorów i ich sygnatury powinny również być notowane, twórz 'patch' od pewnego punktu, po wpisaniu: + + $ git format-patch 1b6d + +Uzyskane w ten sposób dane mogą przekazane być do *git-send-mail* albo odręcznie wysłane. Możesz podać grupę 'commits' + + $ git format-patch 1b6d..HEAD^^ + +Po stronie odbiorcy zapamiętaj e-mail jako daną i podaj: + + $ git am < email.txt + +Patch zostanie wprowadzony i utworzy commit, włącznie z informacjami jak na przykład informacje o autorze. + +Jeśli stosujesz webmail musisz ewentualnie poszukać opcji pokazania treści w formie niesformatowanego textu, zanim zapamiętasz patch do pliku. + +Występują minimalne różnice między aplikacjami e-mailowymi bazującymi na mbox, ale jeśli korzystasz z takiej, należysz do grupy ludzi, która za pewne umie się z nimi obchodzić bez czytania instrukcji! + +=== Przepraszamy, przeprowadziliśmy się === + +Po sklonowaniu repozytorium, polecenia *git push* albo *git pull* będą automatycznie wskazywały na oryginalne URL. Jak Git to robi? Tajemnica leży w konfiguracji, która utworzona zostaje podczas klonowania. Zaryzykujmy spojrzenie: + + $ git config --list + +Opcja +remote.origin.url+ kontroluje źródłowe URL; ``origin'' to alias, nadany źródłowemu repozytorium. Tak jak i przy konwencji z 'master branch', możemy ten alias zmienić albo skasować, zwykle jednak nie ma powodów by to robić. + +Jeśli oryginalne repozytorium zostanie przesunięte, możemy zaktualizować link poprzez: + + $ git config remote.origin.url git://nowy_link/proj.git + +Opcja +branch.master.merge+ definiuje standardowy 'remote-branch' dla *git pull*. Podczas początkowego klonowania, zostanie ustawiony na aktualny branch źródłowego repozytorium, że nawet i po tym jak 'HEAD' źródłowego repozytorium przejdzie do innego branch, późniejszy 'pull' pozostanie wierny oryginalnemu branch. + +Ta opcja jest ważna jedynie dla repozytorium, z którego dokonało się pierwszego klonowania, co zapisane jest w opcji +branch.master.remote+. Przy 'pull' z innego repozytorium musimy podać z którego branch chcemy korzystać. + + $ git pull git://example.com/inny.git master + +To wyjaśnia dlaczego nasze poprzednie przykłady z 'push' i 'pull' nie posiadały argumentów. + +=== Oddalone 'Branches' === + +Jeśli klonujesz repozytorium, klonujesz również wszystkie jego 'branches' Może jeszcze tego nie zauważyłaś, ponieważ Git je ukrywa: musisz się o nie specjalnie pytać: To zapobiega temu, że branches z oddalonego repozytorium nie przeszkadzają twoim lokalnym branches i czyni to Git łatwiejszym dla początkujących. + +Oddalone 'branches' możesz pokazać poprzez: + + $ git branch -r + +Powinieneś zobaczyć coś jak: + +origin/HEAD origin/master origin/experimental + +Lista ta ukazuje branches i HEAD odległego repozytorium, które mogą być również stosowane w zwykłych poleceniach Git. Przyjmijmy, na przykład, że wykonałaś wiele commits i chciałbyś uzyskać porównanie do ostatnio ściągniętej wersji. Możesz przeszukać logi za odpowiednim hashem SHA1, ale dużo prościej jest podać: + + $ git diff origin/HEAD + +Możesz też sprawdzić co działo się w 'branch' ``experimental'': + + $ git log origin/experimental + +=== Więcej serwerów === + +Przyjmijmy, dwóch innych programistów pracuje nad twoim projektem i chciałabyś mieć ich na oku. Możemy obserwować więcej niż jedno repozytorium jednocześnie: + + $ git remote add inny git://example.com/jakies_repo.git + $ git pull inny jakis_branch + +Teraz przyłączyliśmy jeden 'branch' z dwóch repozytoriów i uzyskaliśmy łatwy dostęp do wszystkich 'branch' z wszystkich repozytoriów. + + $ git diff origin/experimental^ inny/jakiś_branch~5 + +Co jednak zrobić, gdy chcemy porównać zmiany w nich bez wpływu na naszą pracę? Innymi słowami, chcemy zbadać ich 'branches' bez importowania ich zmian do naszego katalogu roboczego. Zamiast 'pull' skorzystaj z: + + $ git fetch # Fetch z origin, standard. + $ git fetch inne # Fetch od drugiego programisty. + +Polecenie to załaduje jedynie historię Mimo, że nasz katalog pozostał bez zmian, możemy teraz referować z każdego repozytorium poprzez polecenia Gita, ponieważ posiadamy lokalną kopię. + +Przypomnij sobie, że 'pull' za kulisami to to samo co 'fetch' z następującym za nim *merge*. W normalnym wypadku wykonalibyśmy *pull*, bo chcielibyśmy przywołać również ostatnie 'commmits'. Ta przywołana sytuacja jest wyjątkiem wartym wspomnienia. + +Sprawdź *git help remote* by zobaczyć, jak usuwa się repozytoria, ignoruje pewne branches i więcej. + +=== Moje ustawienia === + +W moich projektach preferuję, gdy pomagający mi programiści przygotują własne repozytoria z których mogę wykonać 'pull'. Większość hosterów Gita pozwala na utworzenie jednym kliknięciem twojego własnego forka innego projektu. + +Gdy przywołałem moją gałęź korzystam z poleceń Gita dla nawigacji i kontroli zmian, które najlepiej, gdy są dobrze zorganizowane i udokumentowane. Wykonuję 'merge' moich własnych zmian i przeprowadzam ewentualnie dalsze zmiany. Gdy już jestem zadowolony, 'push' do zentralnego repozytorium. + +Mimo, iż dość rzadko otrzymuję posty, jestem zdania, że ta metoda się opłaca. Zobacz http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[Post na Blogu Linusa Torvalds (po angielsku)]. + +Pozostając w świecie Gita jest wygodniejsze niż otrzymywanie patchów, ponieważ zaoszczędza mi to konwertowanie ich do 'commits' Gita. Poza tym Git martwi się o szczegóły, jak nazwa autora i adres e-maila, tak samo jak i o datę i godzinę oraz motywuje autora do opisywania swoich zmian. diff --git a/pl/preface.txt b/pl/preface.txt dissimilarity index 86% index c9c7325..a8261a2 100644 --- a/pl/preface.txt +++ b/pl/preface.txt @@ -1,65 +1,58 @@ -= Git Magic = -Ben Lynn -August 2007 - -== Preface == - -http://git-scm.com/[Git] is a version control Swiss army knife. A reliable versatile multipurpose revision control tool whose extraordinary flexibility makes it tricky to learn, let alone master. - -As Arthur C. Clarke observed, any sufficiently advanced technology is indistinguishable from magic. This is a great way to approach Git: newbies can ignore its inner workings and view Git as a gizmo that can amaze friends and infuriate enemies with its wondrous abilities. - -Rather than go into details, we provide rough instructions for particular effects. After repeated use, gradually you will understand how each trick works, and how to tailor the recipes for your needs. - -.Translations - - - link:/\~blynn/gitmagic/intl/zh_cn/[Simplified Chinese]: by JunJie, Meng and JiangWei. Converted to link:/~blynn/gitmagic/intl/zh_tw/[Traditional Chinese] via +cconv -f UTF8-CN -t UTF8-TW+. - - link:/~blynn/gitmagic/intl/fr/[French]: by Alexandre Garel, Paul Gaborit, and Nicolas Deram. Also hosted at http://tutoriels.itaapy.com/[itaapy]. - - link:/~blynn/gitmagic/intl/de/[German]: by Benjamin Bellee and Armin Stebich; also http://gitmagic.lordofbikes.de/[hosted on Armin's website]. - - http://www.slideshare.net/slide_user/magia-git[Portuguese]: by Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[ODT version]]. - - link:/~blynn/gitmagic/intl/ru/[Russian]: by Tikhon Tarnavsky, Mikhail Dymskov, and others. - - link:/~blynn/gitmagic/intl/es/[Spanish]: by Rodrigo Toledo and Ariset Llerena Tapia. - - link:/~blynn/gitmagic/intl/uk/[Ukrainian]: by Volodymyr Bodenchuk. - - link:/~blynn/gitmagic/intl/vi/[Vietnamese]: by Trần Ngọc Quân; also http://vnwildman.users.sourceforge.net/gitmagic/[hosted on his website]. - -.Other Editions - - - link:book.html[Single webpage]: barebones HTML, with no CSS. - - link:book.pdf[PDF file]: printer-friendly. - - http://packages.debian.org/gitmagic[Debian package], http://packages.ubuntu.com/gitmagic[Ubuntu package]: get a fast and local copy of this site. Handy http://csdcf.stanford.edu/status/[when this server is offline]. - - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Physical book [Amazon.com]]: 64 pages, 15.24cm x 22.86cm, black and white. Handy when there is no electricity. - -=== Thanks! === - -I'm humbled that so many people have worked on translations of these pages. I -greatly appreciate having a wider audience because of the efforts of those -named above. - -Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, Tyler Breisacher, Sonia Hamilton, Julian Haagsma, Romain Lespinasse, Sergey Litvinov, Oliver Ferrigni, David Toca, Сергей Сергеев, Joël Thieffry, and Baiju Muthukadan contributed corrections and improvements. - -François Marier maintains the Debian package originally created by Daniel -Baumann. - -John Hinnegan bought the http://www.gitmagic.com/[gitmagic.com] domain. - -My gratitude goes to many others for your support and praise. I'm tempted to -quote you here, but it might raise expectations to ridiculous heights. - -If I've left you out by mistake, please tell me or just send me a patch! - -=== License === - -This guide is released under http://www.gnu.org/licenses/gpl-3.0.html[the GNU General Public License version 3]. Naturally, the source is kept in a Git -repository, and can be obtained by typing: - - $ git clone git://repo.or.cz/gitmagic.git # Creates "gitmagic" directory. - -or from one of the mirrors: - - $ git clone git://github.com/blynn/gitmagic.git - $ git clone git://gitorious.org/gitmagic/mainline.git - $ git clone https://code.google.com/p/gitmagic/ - $ git clone git://git.assembla.com/gitmagic.git - $ git clone git@bitbucket.org:blynn/gitmagic.git - -GitHub, Assembla, and Bitbucket support private repositories, the latter two -for free. += Git Magic = +Ben Lynn +Sierpień 2007 + +== Przedmowa == + +http://git-scm.com/[Git] to rodzaj scyzoryka szwajcarskiego dla kontroli wersji. To niezawodne, wielostronne narzędzie do kontroli wersji o niezwykłej elastyczności przysprawia trudności już w samym jego poznaniu, nie wspominając o opanowaniu. + +Jak stwierdził Arthur C. Clarke, każda wystarczająco postępowa technologia jest porównywalna z magią. Jest to wspaniałe podejście, by zacząć pracę z Gitem: Początkujący mogą zignorować jego wewnętrzne mechanizmy i ujrzeć jako rzecz, która urzeka przyjaciół swoimi niezwykłymi możliwościami, a przeciwników doprowadza do białej gorączki. + +Zamiast wchodzić w szczegóły, oferujemy proste instrukcje dla osiągnięcia zamierzonych efektów. W miarę regularnego korzystania stopniowo sam zrozumiesz jak każda z tych sztuczek funkcjonuje i jak dopasować podane instrukcje na twoje własne potrzeby. + +.Tłumaczenia + + - link:/\~blynn/gitmagic/intl/zh_cn/[Chiński uproszczony]: od JunJie, Meng i JiangWei. Konwertacja do link:/~blynn/gitmagic/intl/zh_tw/[Tradycjonalnego chińskiego] za pomocą +cconv -f UTF8-CN -t UTF8-TW+. + - link:/~blynn/gitmagic/intl/fr/[Francuski]: od Alexandre Garel, Paul Gaborit, i Nicolas Deram. Również hostowany na http://tutoriels.itaapy.com/[itaapy]. + - link:/~blynn/gitmagic/intl/de/[Niemiecki]: od Benjamin Bellee i Armin Stebich; również hostowany na http://gitmagic.lordofbikes.de/[stronie Armina]. + - http://www.slideshare.net/slide_user/magia-git[Portugalski]: od Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[wersja ODT]]. + - link:/~blynn/gitmagic/intl/ru/[Rosyjski]: od Tikhon Tarnavsky, Mikhail Dymskov, i innych. + - link:/~blynn/gitmagic/intl/es/[Hiszpański]: od Rodrigo Toledo i Ariset Llerena Tapia. + - link:/~blynn/gitmagic/intl/uk/[Ukraiński]: od Volodymyr Bodenchuk. + - link:/~blynn/gitmagic/intl/vi/[Wietnamski]: od Trần Ngọc Quân; również hostowany http://vnwildman.users.sourceforge.net/gitmagic/[na tej stronie]. + +.Inne wydania + + - link:book.html[Jako jedna strona]: uszczuplone HTML, bez CSS. + - link:book.pdf[Wersja PDF]: przyjazna w druku. + - http://packages.debian.org/gitmagic[Pakiet Debiana], http://packages.ubuntu.com/gitmagic[Pakiet Ubuntu]: Pobiera szybką i lokalną kopię tej strony. Przydatne http://csdcf.stanford.edu/status/[gdyby ten serwer był offline]. + - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Drukowane wydanie [Amazon.com]]: 64 strony, 15.24cm x 22.86cm, czarno-biały. Przyda się, gdy zabraknie prądu. + + +=== Podziękowania! === + +Jestem mile zaskoczony, że tak dużo ludzi pracowało nad przetłumaczeniem tych stron. Bardzo cenię, iż dzięki staraniom wyżej wspommnianych osób otrzymałem możliwość dotarcia do większego grona czytelników. + +Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, i Tyler Breisacher przyczynilli się do poprawek i korektur. + +François Marier jest mentorem pakietu Debiana, który uprzednio utworzony został przez Daniela Baumann. + +Chciałbym podziękować również wszystkim innym za ich pomoc i dobre słowo. Chciałbym tu wszystkich wyszczególnić, mogłoby to jednak wzbudzić oczekiwania w szerokim zakresie. + +Gdybym o tobie przypadkowo zapomniał, daj mi znać albo przyślij mi po prostu patch. + +=== Licencja === + +Ten poradnik publikowany jest na bazie licencji http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License Version 3]. Oczywiście, tekst źródłowy znajduje się w repozytorium Git i może zostać pobrany przez: + + $ git clone git://repo.or.cz/gitmagic.git # Utworzy katalog "gitmagic". + +albo z serwerów lustrzanych: + + $ git clone git://github.com/blynn/gitmagic.git + $ git clone git://gitorious.org/gitmagic/mainline.git + $ git clone https://code.google.com/p/gitmagic/ + $ git clone git://git.assembla.com/gitmagic.git + $ git clone git@bitbucket.org:blynn/gitmagic.git + +GitHub, Assembla, i Bitbucket pozwalają na prowadzienie prywatnych repozytorii, te dwa ostatnie za darmo. diff --git a/pl/secrets.txt b/pl/secrets.txt dissimilarity index 97% index 4d0aa73..451738a 100644 --- a/pl/secrets.txt +++ b/pl/secrets.txt @@ -1,214 +1,146 @@ -== Secrets Revealed == - -We take a peek under the hood and explain how Git performs its miracles. I will skimp over details. For in-depth descriptions refer to http://schacon.github.com/git/user-manual.html[the user manual]. - -=== Invisibility === - -How can Git be so unobtrusive? Aside from occasional commits and merges, you can work as if you were unaware that version control exists. That is, until you need it, and that's when you're glad Git was watching over you the whole time. - -Other version control systems force you to constantly struggle with red tape and bureaucracy. Permissions of files may be read-only unless you explicitly tell a central server which files you intend to edit. The most basic commands may slow to a crawl as the number of users increases. Work grinds to a halt when the network or the central server goes down. - -In contrast, Git simply keeps the history of your project in the `.git` directory in your working directory. This is your own copy of the history, so you can stay offline until you want to communicate with others. You have total control over the fate of your files because Git can easily recreate a saved state from `.git` at any time. - -=== Integrity === - -Most people associate cryptography with keeping information secret, but another equally important goal is keeping information safe. Proper use of cryptographic hash functions can prevent accidental or malicious data corruption. - -A SHA1 hash can be thought of as a unique 160-bit ID number for every string of bytes you'll encounter in your life. Actually more than that: every string of bytes that any human will ever use over many lifetimes. - -As a SHA1 hash is itself a string of bytes, we can hash strings of bytes containing other hashes. This simple observation is surprisingly useful: look up 'hash chains'. We'll later see how Git uses it to efficiently guarantee data integrity. - -Briefly, Git keeps your data in the `.git/objects` subdirectory, where instead of normal filenames, you'll find only IDs. By using IDs as filenames, as well as a few lockfiles and timestamping tricks, Git transforms any humble filesystem into an efficient and robust database. - -=== Intelligence === - -How does Git know you renamed a file, even though you never mentioned the fact explicitly? Sure, you may have run *git mv*, but that is exactly the same as a *git rm* followed by a *git add*. - -Git heuristically ferrets out renames and copies between successive versions. In fact, it can detect chunks of code being moved or copied around between files! Though it cannot cover all cases, it does a decent job, and this feature is always improving. If it fails to work for you, try options enabling more expensive copy detection, and consider upgrading. - -=== Indexing === - -For every tracked file, Git records information such as its size, creation time and last modification time in a file known as the 'index'. To determine whether a file has changed, Git compares its current stats with those cached in the index. If they match, then Git can skip reading the file again. - -Since stat calls are considerably faster than file reads, if you only edit a -few files, Git can update its state in almost no time. - -We stated earlier that the index is a staging area. Why is a bunch of file -stats a staging area? Because the add command puts files into Git's database -and updates these stats, while the commit command, without options, creates a -commit based only on these stats and the files already in the database. - -=== Git's Origins === - -This http://lkml.org/lkml/2005/4/6/121[Linux Kernel Mailing List post] describes the chain of events that led to Git. The entire thread is a fascinating archaeological site for Git historians. - -=== The Object Database === - -Every version of your data is kept in the 'object database', which lives in the -subdirectory `.git/objects`; the other residents of `.git/` hold lesser data: -the index, branch names, tags, configuration options, logs, the current -location of the head commit, and so on. The object database is elementary yet -elegant, and the source of Git's power. - -Each file within `.git/objects` is an 'object'. There are 3 kinds of objects -that concern us: 'blob' objects, 'tree' objects, and 'commit' objects. - -=== Blobs === - -First, a magic trick. Pick a filename, any filename. In an empty directory: - - $ echo sweet > YOUR_FILENAME - $ git init - $ git add . - $ find .git/objects -type f - -You'll see +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. - -How do I know this without knowing the filename? It's because the -SHA1 hash of: - - "blob" SP "6" NUL "sweet" LF - -is aa823728ea7d592acc69b36875a482cdf3fd5c8d, -where SP is a space, NUL is a zero byte and LF is a linefeed. You can verify -this by typing: - - $ printf "blob 6\000sweet\n" | sha1sum - -Git is 'content-addressable': files are not stored according to their filename, -but rather by the hash of the data they contain, in a file we call a 'blob -object'. We can think of the hash as a unique ID for a file's contents, so -in a sense we are addressing files by their content. The initial `blob 6` is -merely a header consisting of the object type and its length in bytes; it -simplifies internal bookkeeping. - -Thus I could easily predict what you would see. The file's name is irrelevant: -only the data inside is used to construct the blob object. - -You may be wondering what happens to identical files. Try adding copies of -your file, with any filenames whatsoever. The contents of +.git/objects+ stay -the same no matter how many you add. Git only stores the data once. - -By the way, the files within +.git/objects+ are compressed with zlib so you -should not stare at them directly. Filter them through -http://www.zlib.net/zpipe.c[zpipe -d], or type: - - $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d - -which pretty-prints the given object. - -=== Trees === - -But where are the filenames? They must be stored somewhere at some stage. -Git gets around to the filenames during a commit: - - $ git commit # Type some message. - $ find .git/objects -type f - -You should now see 3 objects. This time I cannot tell you what the 2 new files are, as it partly depends on the filename you picked. We'll proceed assuming you chose ``rose''. If you didn't, you can rewrite history to make it look like you did: - - $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose' - $ find .git/objects -type f - -Now you should see the file -+.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, because this is the -SHA1 hash of its contents: - - "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d - -Check this file does indeed contain the above by typing: - - $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch - -With zpipe, it's easy to verify the hash: - - $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum - -Hash verification is trickier via cat-file because its output contains more -than the raw uncompressed object file. - -This file is a 'tree' object: a list of tuples consisting of a file -type, a filename, and a hash. In our example, the file type is 100644, which -means `rose` is a normal file, and the hash is the blob object that contains -the contents of `rose'. Other possible file types are executables, symlinks or -directories. In the last case, the hash points to a tree object. - -If you ran filter-branch, you'll have old objects you no longer need. Although -they will be jettisoned automatically once the grace period expires, we'll -delete them now to make our toy example easier to follow: - - $ rm -r .git/refs/original - $ git reflog expire --expire=now --all - $ git prune - -For real projects you should typically avoid commands like this, as you are -destroying backups. If you want a clean repository, it is usually best to make -a fresh clone. Also, take care when directly manipulating +.git+: what if a Git -command is running at the same time, or a sudden power outage occurs? -In general, refs should be deleted with *git update-ref -d*, -though usually it's safe to remove +refs/original+ by hand. - -=== Commits === - -We've explained 2 of the 3 objects. The third is a 'commit' object. Its -contents depend on the commit message as well as the date and time it was -created. To match what we have here, we'll have to tweak it a little: - - $ git commit --amend -m Shakespeare # Change the commit message. - $ git filter-branch --env-filter 'export - GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" - GIT_AUTHOR_NAME="Alice" - GIT_AUTHOR_EMAIL="alice@example.com" - GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" - GIT_COMMITTER_NAME="Bob" - GIT_COMMITTER_EMAIL="bob@example.com"' # Rig timestamps and authors. - $ find .git/objects -type f - -You should now see -+.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ -which is the SHA1 hash of its contents: - - "commit 158" NUL - "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF - "author Alice 1234567890 -0800" LF - "committer Bob 1234567890 -0800" LF - LF - "Shakespeare" LF - -As before, you can run zpipe or cat-file to see for yourself. - -This is the first commit, so there are no parent commits, but later commits -will always contain at least one line identifying a parent commit. - -=== Indistinguishable From Magic === - -Git's secrets seem too simple. It looks like you could mix together a few shell scripts and add a dash of C code to cook it up in a matter of hours: a melange of basic filesystem operations and SHA1 hashing, garnished with lock files and fsyncs for robustness. In fact, this accurately describes the earliest versions of Git. Nonetheless, apart from ingenious packing tricks to save space, and ingenious indexing tricks to save time, we now know how Git deftly changes a filesystem into a database perfect for version control. - -For example, if any file within the object database is corrupted by a disk -error, then its hash will no longer match, alerting us to the problem. By -hashing hashes of other objects, we maintain integrity at all levels. Commits -are atomic, that is, a commit can never only partially record changes: we can -only compute the hash of a commit and store it in the database after we already -have stored all relevant trees, blobs and parent commits. The object -database is immune to unexpected interruptions such as power outages. - -We defeat even the most devious adversaries. Suppose somebody attempts to -stealthily modify the contents of a file in an ancient version of a project. To -keep the object database looking healthy, they must also change the hash of the -corresponding blob object since it's now a different string of bytes. This -means they'll have to change the hash of any tree object referencing the file, -and in turn change the hash of all commit objects involving such a tree, in -addition to the hashes of all the descendants of these commits. This implies the -hash of the official head differs to that of the bad repository. By -following the trail of mismatching hashes we can pinpoint the mutilated file, -as well as the commit where it was first corrupted. - -In short, so long as the 20 bytes representing the last commit are safe, -it's impossible to tamper with a Git repository. - -What about Git's famous features? Branching? Merging? Tags? -Mere details. The current head is kept in the file +.git/HEAD+, -which contains a hash of a commit object. The hash gets updated during a commit -as well as many other commands. Branches are almost the same: they are files in -+.git/refs/heads+. Tags too: they live in +.git/refs/tags+ but they -are updated by a different set of commands. +== Uchylenie tajemnicy == + +Rzućmy spojrzenie pod maskę silnika i wytłumaczymy w jaki sposób Git realizuje swoje cuda. Nie będę wchodził w szczegóły. Dla pogłębienia tematu odsyłam na http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[anielskojęzychny podręcznik użytkownika]. + +=== Niewidzialność === + +Jak to możliwe, że Git jest taki niepostrzeżony? Zapominając na chwilę o sporadycznych 'commits' i 'merges', możesz pracować w sposób, jakby kontrola wersji w ogóle nie istniała. Chciałem powiedzieć, do czasu aż będzie ci potrzebna. A oto chodzi, byś był zadowolony z tego, że Git cały czas czuwa nad twoją pracą. + +Inne systemy kontroli wersji ciągle zmuszają cię do ciągłego borykania się z zagadnieniem samej kontroli i związanej z tym biurokracji. Pliki mogą być zabezpieczone przed zapisem, aż do momentu gdy uda ci się poinformować centralny serwer o tym, że chciałabyś nad nimi popracować. Przy wzroście liczby użytkowników nawet najprostsze polecenia stają się wolne jak ślimak. Gdy tylko zniknie sieć lub centralny serwer praca staje. + +W przeciwieństwie do tego, Git posiada kronikę całej swojej historii w podkatalogu .git twojego katalogu roboczego. Jest to twoja własna kopie całej historii, z którą mogłabyś pracować offline, aż do momentu gdy zechcesz wymienić dane z innymi. Posiadasz absolutną kontrolę nad losem twoich danych, ponieważ Git potrafi dla ciebie w każdej chwili odtworzyć zapamiętany poprzednio stan z właśnie podkatalogu .git. + +=== Integralność === + +Z kryptografią przez większość ludzi łączona jest poufność informacji, jednak równie ważnym jej celem jest zabezpieczenie danych. Właściwe zastosowanie kryptograficznych funkcji hashujących (funkcji skrótu) może uchronić przed nieumyślnym lub celowym zniszczeniem danych. + +Klucz hashujący SHA1 mogłabyś wyobrazić sobie jako składający się ze 160 bitów numer identyfikacyjny jednoznacznie opisujący dowolny łańcuch znaków, i który spotkasz w sowim życiu jeden jedyny raz. Nawet i więcej niż to: wszystkie łańcuchy znaków, jakie ludzkość przez wiele generacji stworzyła. + +Sama suma konreolna SHA1 też jest łańcuchem znaków w formie bajtów. Możemy generować hashe SHA1 z łańcuchów samych zawierających inne hashe SHA1. Ta prosta obserwacja okazała się niesamowicie pożyteczna: jeśli cię to zainteresowało poszukaj informacji na temat 'hash chains'. Zobaczymy później w jaki sposób wykorzystuje je Git dla zapewnienia produktywności i integralności danych. + +Krótko mówiąc, Git przechowuje twoje dane w podkatalogu `.git/objects`, gdzie zamiast nazw plików znajdziesz numery identyfikacyjne. Poprzez wykorzystanie tych numerów identyfikacyjnych jako nazwy plików razem z kilkoma innymi trikami związanymi z plikami blokującymi i znacznikami czasu, Git zamienia twój prosty system plików na produktywną i solidną bazę danych. + +=== Inteligencja === + +Skąd Git wie o tym, że zmieniłaś nazwę jakiegoś pliku, jeśli nigdy go o tym wyraźnie nie poinformowałaś? Oczywiście, być może użyłaś polecenia *git mv*, jest to jednak to samo jakbyś użyła *git rm*, a następnie *git add*. + +Git poszukuje heurystycznie zmian nazw w następujących po sobie wersjach kopii. Dodatkowo potrafi czasami nawet znaleźć całe bloki z kodem przenoszonym tam i z powrotem między plikami! Mimo iż wykonuje kawał dobrej roboty, a ta właściwość staje się coraz lepsza, nie potrafi niestety jeszcze poradzić sobie z wszystkimi możliwymi przypadkami. Jeśli to u ciebie nie działa, spróbuj poszukać opcji rozszerzonego rozpoznawania kopii, aktualizacja samego Gita, też może pomóc. + +=== Indeksowanie === + +Dla każdego kontrolowanego pliku, Git zapamiętuje informacje o jego wielkości, czasie utworzenia i czasie ostatniej edycji w pliku znanym nam jako indeks. By ustalić, czy nastąpiła jakaś zmiana, Git porównuje stan aktualny ze stanem zapamiętanym w indeksie. Jeśli dane te nie różnią się, Git może pominąć czytanie zawartości pliku. + +Ponieważ sprawdzenie statusu pliku trwa dużo krócej niż jego całkowite wczytanie, to jeśli dokonałaś zmian tylko na kilku plikach Git zaktualizuje swój stan w mgnieniu oka. + +Stwierdziliśmy już wcześniej, że indeks jest przechowalnią (ang. staging area). Jak to możliwe, że stos informacji o statusie danych może być przechowalnią? Ponieważ polecenie 'add' transportuje pliki do bazy danych Git i aktualizuje informacje o ich statusie, podczas gdy polecenie 'commit' (bez opcji) tworzy commit tylko wyłącznie na podstawie informacji o statusie plików, ponieważ pliki te już się w tej bazie znajdują. + +=== Korzenie Git === + +Ten http://lkml.org/lkml/2005/4/6/121['Linux Kernel Mailing List' post] opisuje cały łańcuch zdarzeń, które inicjowały powstanie Git. Cały post jest archeologicznie fascynującą stroną dla historyków zajmujących się Gitem. + +=== Obiektowa baza danych === + +Każda wersja twoich danych jest przechowywana w obiektowej bazie danych, która znajduje się w podkatalogu `.git/objects`. Inne miejsca w `.git/` posiadają mniej ważne dane, jak indeks, nazwy gałęzi ('branch'), tagi, logi, konfigurację, aktualną pozycję HEAD i tak dalej. Obiektowa baza danych jest prosta, mimo to jednak elegancka i jest źródłem siły Gita. + +Każdy plik w `.git/objects` jest obiektem. Istnieją trzy rodzaje obiektów, które nas interesują: 'blob', 'tree' i 'commit'. + +=== Bloby === + +Na początek magiczna sztuczka. Wymyśl jakąś nazwę pliku, jakąkolwiek. W pustym katalogu: + + + $ echo sweet > TWOJA_NAZWA + $ git init + $ git add . + $ find .git/objects -type f + $ find .git/objects -type f + +Zobaczysz coś takiego: +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. + +Skąd mogłem to wiedzieć, mimo iż nie znałem nazwy pliku? Ponieważ suma kontrolna SHA1 dla: + + "blob" SP "6" NUL "sweet" LF + +wynosi właśnie: aa823728ea7d592acc69b36875a482cdf3fd5c8d. Przy czym SP to spacja, NUL - to bajt zerowy, a LF to znak nowej linii ('newline'). Możesz to skontrolować wpisując: + + $ printf "blob 6\000sweet\n" | sha1sum + +Git pracuje asocjacyjnie (skojarzeniowo): dane nie są zapamiętywane na podstawie ich nazwy, tylko wartości ich własnego hasha SHA1 w pliku, który określamy mianem obiektu 'blob'. Sumę kontrolną SHA1 możemy sobie wyobrazić jako niepowtarzalny numer identyfikacyjny zawartości pliku, co oznacza, że pliki adresowane są na podstawie ich zawartości. Początkowe `blob 6`, to jedynie adnotacja, która określa tylko rodzaj obiektu i jego wielkość w bajtach, pozwala to na uproszczenie zarządzania wewnętrznego. + +Przez to właśnie mogłem 'przepowiedzieć' wynik. Nazwa pliku nie ma znaczenia, jedynie jego zawartość służy do utworzenia obiektu 'blob'. + +Pytasz się, a co w przypadku identycznych plików? Spróbuj dodać kopie twojej danej pod jakąkolwiek nazwą. Zawartość +.git/objects+ nie zmieni się, niezależnie ile kopii dodałaś. Git zapamięta zawartość pliku wyłącznie jeden raz. + +Na marginesie, dane w +.git/objects+ są spakowane poprzez 'zlib', nie powinieneś otwierać ich bezpośrednio. Przefiltruj je najpierw przez http://www.zlib.net/zpipe.c[zpipe -d], albo wpisz: + + $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d + +polecenie to pokaże ci zawartość obiektu jako tekst. + +=== 'Trees' === + +Gdzie są więc nazwy plików? Przecież muszą być gdzieś zapisane. Podczas wykonywania 'commit' Git troszczy się o nazwy plików: + + $ git commit # dodaj jakiś opis. + $ find .git/objects -type f + $ find .git/objects -type f + +Powinieneś ujrzeć teraz 3 obiekty. Tym razem nie jestem w stanie powiedzieć, jak nazywają się te dwa nowe pliki, ponieważ częściowo są zależne od nazwy jaką nadałaś plikom. Pójdźmy dalej, zakładając, że jedną z tych danych nazwałaś ``rose''. Jeśli nie, możesz zmienić opis, by wyglądał jakby był twój: + + $ git filter-branch --tree-filter 'mv TWOJA_NAZWA rose' + $ find .git/objects -type f + +Powinnaś zobaczyć teraz plik +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, ponieważ jest to suma kontrolna SHA1 jego zawartości. + +"tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d + +Sprawdź, czy plik na prawdę odpowiada powyższej zawartości przez polecenie: + + $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch + +Za pomocą 'zpipe' łatwo sprawdzić hash SHA1: + + + $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum + +Sprawdzanie za pomocą 'cat-file' jest troszeczkę kłopotliwe, bo jego 'output' zawiera więcej niż tylko nieskomprymowany obiekt pliku. + +Nasz plik to tak zwany obiekt 'tree': lista wyrażeń, na którą składają się rodzaj pliku, jego nazwa i jego suma kontrolna SHA1. W naszym przykładzie typ pliku to 100644, co oznacza, że `rose` jest plikiem zwykłym, natomiast hash SHA1 odpowiada sumie kontrolnej SHA1 obiektu 'blob' zawierającego zawartość `rose`. Inne możliwe rodzaje plików to programy, linki symboliczne i katalogi. W ostatnim przypadku hash SHA1 wskazuje na obiekt 'tree'. + +Jeśli użyjesz polecenia 'filter-branch', otrzymasz stare objekty, które nie są już używane. Mimo iż automatycznie zostaną usunięte po upłynięciu okresu karencji, chcemy się ich pozbyć od zaraz, aby lepiej prześledzić następne przykłady. + + $ rm -r .git/refs/original + $ git reflog expire --expire=now --all + $ git prune + +W prawdziwych projektach powinnaś unikać takich komend, ponieważ zniszczą zabezpieczone dane. Jeśli chcesz posiadać czyste repozytorium, to najlepiej załóż nowy klon. Bądź też ostrożna przy bezpośredniej manipulacji +.git+: gdy równocześnie wykonywane jest polecenie Git i zgaśnie światło? Generalnie do kasowania referencji powinnaś używać *git update-ref -d*, nawet gdy ręczne usunięcie +ref/original+ jest dość bezpieczne. + +=== 'Commits' === + +Wytłumaczyliśmy dwa z trzech obiektów. Ten trzeci to obiekt 'commit' Jego zawartość jest zależna od opisu 'commit' jak i czasu jego wykonania. By wszystko do naszego przykładu pasowało, musimy trochę pokombinować. + + $ git commit --amend -m Shakespeare # Zmień ten opis. + $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Zmanipuluj znacznik czasowy i nazwę autora. + $ find .git/objects -type f + $ find .git/objects -type f + +Powinieneś znaleźć +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+, co odpowiada sumie kontrolnej SHA1 jego zawartości: + +"commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice 1234567890 -0800" LF "committer Bob 1234567890 -0800" LF LF "Shakespeare" LF + +Jak i w poprzednich przykładach możesz użyć 'zpipe' albo 'cat-file' by to sprawdzić. + +To jest pierwszy 'commit', przez to nie posiada matczynych 'commits'. Następujące 'commits' będą zawsze zawierać przynajmniej jedną linikę identyfikującą rodzica. + +=== Nie do odróżnienia od magii === + +Tajemnice Gita wydają się być proste. Wygląda to jak połączenie kilku skryptów, troszeczkę kodu C i w przeciągu kilku godzin jesteśmy gotowi: zmiksowanie podstawowych operacji na systemie danych, obliczenia SHA1, przyprawienie plikami blokującymy i synchronizacją dla stabilności. W sumie można by tak opisać najwcześniejsze wersje Gita. Tym niemniej, abstrahując od udanych trików pakujących, by oszczędnie odnosić się z pamięcią i udanych trików indeksujących by zaoszczędzić czas, wiemy jak Git sprawnie przemienia system danych w objektową bazę danych, co jest optymalne dla kontroli wersji. + +Przyjmijmy, gdy jakikolwiek plik w obiektowej bazie danych ulegnie zniszczeniu poprzez błąd nośnika, to jego SHA1 nie będzie zgadzać się z jego zawartością, co od razu wskaże nam problem. Poprzez tworzenie kluczy SHA1 z kluczy SHA1 innych objektów, osiągniemy integralność danych na wszystkich poziomach. 'Commits' są elementarne, to znaczy, 'commit' nie potrafi zapamiętać jedynie części zmian: hash SHA1 'commit' możemy obliczyć i zapamiętać dopiero po tym gdy zapamiętane zostały wszystkie obiekty 'tree', 'blob' i rodziców 'commit'. Obiektowa baza dynch jest odporna na nieoczekiwane przerwy, jak na przykład przerwanie dostawy prądu. + +Możemy przetrwać nawet podstępnego przeciwnika. Wyobraź sobie, ktoś ma zamiar zmienić treść jakiegoś pliku, która leży w jakiejś starszej wersji projektu. By sprawić pozory, że baza danych wygląda nienaruszona musiałby zmienić sumy kontrolne SHA1 korespondujących obiektów, ponieważ plik zawiera teraz zmieniony sznur znaków. To znaczy również, że musiałby zmienić każdy hash obiektu 'tree', które ją referują oraz w wyniku tego wszystkie sumy kontrolne 'commits' zawierające obiekty 'tree' dodatkowo do pochodnych tych 'commits'. Oznacza to również, że suma kontrolna oficjalnego HEAD różni się od sumy kontrolnej HEAD manipulowanego repozytorium. Wystarczy teraz prześledzić ścieżkę różniących się hashy SHA1, odnaleźć okaleczony plik, jak i 'commit' w którym po raz pierwszy wystąpił. + +Krótko mówiąc, dopuki reprezentujące ostatni commit 20 bajtów są zabezpieczone, sfałszowanie repozytorium Gita nie jest możliwe. + +A co ze sławnymi możliwościami Gita? +'Branching'? 'Merging'? 'Tags'? To szczegół. Aktualny HEAD przetrzymywany jest w pliku +.git/HEAD+, która posiada hash SHA1 ostatniego 'commit'. Hash SHA1 zostaje aktualizowany podczas wykonania 'commit', tak samo jak i przy wielu innych poleceniach. 'branches' to prawie to samo, są plikami zapamiętanymi w +.git/refs/heads+. 'Tags' również, znajdziemy je w +.git/refs/tags+, są one jednak aktualizowane poprzez serię innych poleceń. diff --git a/pl/translate.txt b/pl/translate.txt dissimilarity index 99% index d1842cd..4081d23 100644 --- a/pl/translate.txt +++ b/pl/translate.txt @@ -1,33 +1,21 @@ -== Appendix B: Translating This Guide == - -I recommend the following steps for translating this guide, so my scripts can -quickly produce HTML and PDF versions, and all translations can live in the -same repository. - -Clone the source, then create a directory corresponding to the target -language's IETF tag: see -http://www.w3.org/International/articles/language-tags/Overview.en.php[the W3C -article on internationalization]. For example, English is "en" and Japanese is -"ja". In the new directory, and translate the +txt+ files from the "en" -subdirectory. - -For instance, to translate the guide into http://en.wikipedia.org/wiki/Klingon_language[Klingon], you might type: - - $ git clone git://repo.or.cz/gitmagic.git - $ cd gitmagic - $ mkdir tlh # "tlh" is the IETF language code for Klingon. - $ cd tlh - $ cp ../en/intro.txt . - $ edit intro.txt # Translate the file. - -and so on for each text file. - -Edit the Makefile and add the language code to the `TRANSLATIONS` variable. -You can now review your work incrementally: - - $ make tlh - $ firefox book-tlh/index.html - -Commit your changes often, then let me know when they're ready. -GitHub has an interface that facilitates this: fork the "gitmagic" project, -push your changes, then ask me to merge. +== Załącznik B: Przetłumaczyć to HOWTO == + +Aby przetłumaczyć moje HOWTO polecam wykonanie następujących poniżej kroków, wtedy moje skrypty będą w prosty sposób mogły wygenerować wersje HTML i PDF. Poza tym wszystkie tłumaszenia mogą być prowadzone w jednym repozytorium. + +Sklonuj texty źródłowe, następnie utwórz katalog o nazwie skrótu IETF przetłumaszonego języka: sprawdź http://www.w3.org/International/articles/language-tags/Overview.en.php[Artykół W3C o internacjonalizacji]. Na przykład, angielski to "en", a japoński to "ja". Skopiuj wszystkie pliki +txt+ z katalogu "en" do nowoutworzonego katalogu. + +Aby przykładowo przetłumaczyć to HOWTO na http://de.wikipedia.org/wiki/Klingonische_Sprache[Klingonisch], musisz wykonać następujące polecenia: + + $ git clone git://repo.or.cz/gitmagic.git + $ cd gitmagic + $ mkdir tlh # "tlh" jest skrótem IETF języka Klingonisch. + $ cd tlh $ cp ../en/intro.txt . + $ edit intro.txt # Przetłumacz ten plik. + +i zrób to z każdą następną daną textową. + +Edytuj Makefile i dodaj skrót języka do zmiennej `TRANSLATIONS`. Teraz możesz swoją pracę w każdej chwili sprawdzić: + + $ make tlh $ firefox book-tlh/index.html + +Używaj często 'commit' a gdy już skończysz, to daj znać. GitHub posiada interfejs, który to ułatwi: utwórz twój własny 'Fork' projektu "gitmagic", 'push' twoje zmiany i daj mi znać, by je 'mergen'. diff --git a/szl/basic.txt b/szl/basic.txt new file mode 100644 index 0000000..043ed27 --- /dev/null +++ b/szl/basic.txt @@ -0,0 +1,196 @@ +== Piyrsze szryty == + +Zanim utopisz sie w morzu polecyń Gita, zyrknij nojpiyrw na pora komyndow Gita. Choć som ajnfachowe, to som ważne i sie wnet przidajom. Powiym prowda, jak zaczłech pracować z Git, to przez pora miesiyncy niy potrzebowołech żodnych innych polecyń, kierch byś sam niy znaloz, w tym rozdziale. + +=== Zicherowanie łobecnego stanu === + +Chcołbyś drapko zabrać sie do roboty? Zrob piyrw zicherung danych twojego roboczego kataloga. + + $ git init + $ git add . + $ git commit -m "Mój pierwszy commit" + +Jakbyś potym coś spaproł, możesz pujś nazot do piyrwotnyj wersji. + + $ git reset --hard + +Jakbyś chcioł tyn stan teraz zapamiyntać: + + $ git commit -a -m "Mój następny commit" + +=== Dodanie, kasowanie i zmiana nazwy === +=== Dodanie, kasowanie i zmiynianie nazwy === + +Ta komynda zatrzimo pliki, kiere żeś dodoł za piyrszym razym przy *git add*. A jak mosz jakieś nowe pliki, dej tyż ło tym znać Gitowi. + + $ git add readme.txt Dokumentacja + +To samo, jak byś chcioł, żeby Git zapomnioł ło jakichś plikach: + + $ git rm ramsch.h archaiczne.c + $ git rm -r obciążający/materiał/ + +Jak byś tego jeszcze som niy zrobił, to Git wyciepnie pliki za ciebie. + +Zmiyniynie nazwy plika, to to samo co wyciepanie go i skudzynie nazot pod innom nazwom. Git biere sam skrot *git mv*, kiery mo ta samo składnia co komynda *mv*. Na przikład: + + $ git mv bug.c feature.c + +=== Zaawansowane anulowanie/prziwrocanie === + +Czasym chciołbyś ajnfach ino sie nazot w czasie cofnońć i zapomnieć o zmianach kiere żeś potym wciepoł. Komynda: + + $ git log + +pokoże ci lista 'commits' i ich sum kontrolnych SHA1: +---------------------------------- +commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob +Date: Tue Mar 14 01:59:26 2000 -0800 + + Zamień printf() mit write(). + +commit 82f5ea346a2e651544956a8653c0f58dc151275c +Author: Alicja +Date: Thu Jan 1 00:00:00 1970 +0000 + + Initial commit. +---------------------------------- + +Kilka początkowych znaków klucza SHA1 wystarcza by jednoznacznie zidentyfikować 'commit', alternatywnie możesz skopiować i wkleić cały klucz. Wpisując: + + $ git reset --hard 766f + +przywrócisz stan do stanu podanego 'commit', a wszystkie późniejsze zmiany zostaną bezpowrotnie skasowane. + +Innym razem chcesz tylko na moment przejść do jednego z poprzednich stanów. W tym wypadku użyj komendy: + + $ git checkout 82f5 + +Tym poleceniem wrócisz się w czasie zachowując nowsze zmiany. Ale, tak samo jak w podróżach w czasie z filmów science-fiction - jeśli teraz dokonasz zmian i zapamiętasz je poleceniem 'commit', zostaniesz przeniesiona do alternatywnej rzeczywistości, ponieważ twoje zmiany różnią się od dokonanych w późniejszych punktach czasu. + +Tą alternatywną rzeczywistość nazywamy 'branch', a <>. Na razie, zapamiętaj tylko, że: + + $ git checkout master + +sprowadzi cię znów do teraźniejszości. Również, aby uprzedzić narzekanie Gita, powinieneś przed każdym 'checkout' wykonać 'commit' lub 'reset'. + +Korzystając ponownie z analogii do gier komputerowych: + +- *`git reset --hard`*: załaduj jakiś starszy stan gry i skasuj wszystkie nowsze niż właśnie ładowany. + +- *`git checkout`*: Załaduj stary stan, grając dalej, twój stan będzie się różnił od nowszych zapamiętanych. Każdy stan, który zapamiętasz od teraz, powstanie w osobnym 'branch', reprezentującym alternatywną rzeczywistość. <> + +Jeśli chcesz, możesz przywrócić jedynie wybrane pliki lub katalogi poprzez dodanie ich nazw do polecenia + + $ git checkout 82f5 jeden.plik inny.plik + +Bądź ostrożny, ten sposób użycia komendy *checkout* może bez uprzedzenia skasować pliki. Aby zabezpieczyć eis przed takimi wypadkami powinieneś zawsze wykonać polecenie 'commit' zanim wykonasz 'checkout', szczególnie w okresie poznawania Gita. Jeśli czujesz się niepewnie przed wykonaniem jakiejś operacji Gita, generalną zasadą powinno stać się dla ciebie uprzednie wykonanie *git commit -a*. + +Nie lubisz kopiować i wklejać kluczy SHA1? Możesz w tym wypadku skorzystać z: + + $ git checkout :/"Mój pierwszy c" + +by przenieś się do 'commit', którego opis rozpoczyna zawartą wiadomość. Możesz również cofnąć się do piątego z ostatnio zapamiętanych 'commit': + + $ git checkout master~5 + +=== Przywracanie === + +W sali sądowej pewne zdarzenia mogą zostać wykreślone z akt. Podobnie możesz zaznaczyć pewne 'commits' do wykreślenia. + + $ git commit -a + $ git revert 1b6d + +To polecenie wymaże 'commit' o wybranym kluczu. ten rewers zostanie zapamiętany jako nowy 'commit', co można sprawdzić poleceniem *git log*. + +=== Generowanie listy zmian === + +Niektóre projekty wymagają http://en.wikipedia.org/wiki/Changelog[pliku changelog]. Wygenerujesz go poleceniem: + + $ git log > Changelog + +=== Ładowanie plików === + +Kopię projektu zarządzanego za pomocą Gita uzyskasz poleceniem: + + $ git clone git://ścieżka/do/projektu + +By na przykład zładować wszystkie dane, których użyłem do stworzenia tej strony skorzystaj z: + + $ git clone git://git.or.cz/gitmagic.git + +Do polecenia 'clone' wrócimy niebawem. + +=== Najnowszy stan === + +Jeśli posiadasz już kopię projektu wykonaną za pomocą *git clone*, możesz ją zaktualizować poleceniem: + + $ git pull + +=== Szybka publikacja === + +Przypuśćmy, że napisałeś skrypt i chcesz go udostępnić innym. Mogłabyś poprosić ich, by zładowali go bezpośrednio z twojego komputera. Jeśli jednak zrobią podczas gdy gdy ty wprowadzasz poprawki lub eksperymentujesz ze zmianami, mogłabyś przysporzyć im nieprzyjemności. Z tego powodu istnieje coś takiego jak cykl wydawniczy. Programiści regularnie pracują nad projektem, upubliczniają kod jednak dopiero, jeśli uznają, że nadaje się już do pokazania. + +Aby wykonać to za pomocą GIT, wejdź do katalogu w którym znajduje się twój skrypt: + + $ git init + $ git add . + $ git commit -m "Pierwsze wydanie" + +Następnie poproś twych użytkowników o wykonanie: + + $ git clone twój.komputer:/ścieżka/do/skryptu + +by zładować twój skrypt. Zakładamy tu posiadanie przez nich klucza SSH do twojego komputera. Jeśli go nie mają, uruchom *git daemon* i podaj im następujący link: + + $ git clone git://twój.komputer/ścieżka/do/skryptu + +Od teraz, zawsze gdy uznasz, że wersja nadaje się do opublikowania, wykonaj polecenie: + + $ git commit -a -m "Następna wersja" + +a twoi użytkownicy, po wejściu do katalogu zawierającym twój skrypt, będą go mogli zaktualizować poprzez: + + $ git pull + +Twoi użytkownicy nigdy nie wejdą w posiadanie wersji, których nie chcesz im udostępniać. + +=== A co robiłem ostatnio? === + +Jeśli chcesz zobaczyć zmiany, które wprowadziłeś of ostatniego 'commit', wpisz: + + $ git diff + +Albo tylko zmiany od wczoraj: + + $ git diff "@{yesterday}" + +Albo miedzy określoną wersją i dwoma poprzedzającymi: + + $ git diff 1b6d "master~2" + +Za każdym razem uzyskane informacje są równocześnie patchem, który poprzez *git apply* może zostać być zastosowany. Spróbuj również: + + $ git whatchanged --since="2 weeks ago" + +Jeśli chcę sprawdzić listę zmian jakiegoś repozytorium, często korzystam często z http://sourceforge.net/projects/qgit[qgit], ze względu na jego fotogeniczny interfejs, albo z http://jonas.nitro.dk/tig/[tig], tekstowy interfejs, działający zadowalająco gdy mamy do czynienia z wolnym łączem internetowym. Alternatywnie, zainstaluj serwer http, uruchom *git instaweb*, i odpal dowolną przeglądarkę internetową. + +=== Ćwiczenie === + +Niech A, B, C i D będą 4 następującymi po sobie 'commits', gdzie B różni się od A, jedynie tym, iż usunięto kilka plików. Chcemy teraz te usunięte pliki zrekonstruować do D. Jak to można zrobić? + +Istnieją przynajmniej 3 rozwiązania. Załóżmy że znajdujemy się obecnie w D: + +1. Różnica pomiędzy A i B, to skasowane pliki. Możemy utworzyć patch, który pokaże te różnice i następnie zastosować go: + + $ git diff B A | git apply + +2. Ponieważ pliki zostały już raz zapamiętane w A, możemy je przywrócić: + + $ git checkout A foo.c bar.h + +3. Możemy też widzieć przejście z A na B jako zmianę, którą można zrewertować: + + $ git revert B + +A które z tych rozwiązań jest najlepsze? To, które najbardziej tobie odpowiada. Korzystając z Git łatwo osiągnąć cel, czasami prowadzi do niego wiele dróg. diff --git a/szl/branch.txt b/szl/branch.txt new file mode 100644 index 0000000..858c70e --- /dev/null +++ b/szl/branch.txt @@ -0,0 +1,190 @@ +== Magia branch == + +Szybkie, natychmiastowe działanie poleceń branch i merge, to jedne z najbardziej zabójczych właściwości Gita. + +*Problem*: Zewnętrzne faktory narzucają konieczność zmiany kontekstu. Poważne błędy w opublikowanej wersji ujawniły się bez ostrzeżenia. Skrócono termin opublikowania pewnej właściwości. Autor, którego pomocy potrzebujesz w jednej z kluczowych sekcji postanawia opóścić projekt. W wszystkich tych przypadkach musisz natychmiastowo zaprzestać bieżących prac i skoncentrować się nad zupełnie innymi zadaniami. + +Przerwanie toku myślenia nie jest dobre dla produktywności, a czym większa różnica w kontekście, tym większe straty. Używając centralnych systemów kontroli wersji musielibyśmy najpierw zładować świeżą kopię roboczą z serwera. W systemach rozproszonych wygląda to dużo lepiej, ponieważ możemy żądaną wersje sklonować lokalnie + +Jednak klonowanie również niesie za sobą kopiowanie całego katalogu roboczego jak i całej historii projektu aż do żądanego punktu. Nawet jeśli Git redukuje wielkość przez podział danych i użycie twardych dowiązań, wszystkie pliki projektu muszą zostać odtworzone w nowym katalogu roboczym. + +*Rozwiązanie*: Git posiada lepsze narzędzia dla takich sytuacji, jest ono wiele szybsze i zajmujące mniej miejsca na dysku jak klonowanie: *git branch*. + +Tym magicznym słowem zmienisz dane w swoim katalogu roboczym z jednej wersji w inną. Ta przemiana potrafi dużo więcej jak tylko poruszać się w historii projektu. Twoje pliki mogą przekształcić się z aktualnej wersji do wersji eksperymentalnej, do wersji testowej, do wersji twojego kolegi i tak dalej. + +=== Przycisk 'szef' === + +Być może grałeś już kiedyś w grę, która posiadała magiczny (``przycisk szef''), po naciśnięciu którego twój monitor natychmiast pokazywał jakiś arkusz kalkulacyjny, czy coś tam? Celem przycisku było szybkie ukrycie gierki na wypadek pojawienia się szefa w twoim biurze. + +W jakimś katalogu: + + $ echo "Jestem mądrzejszy od szefa" > mój_plik.txt + $ git init + $ git add . + $ git commit -m "Pierwszy commit" + +Utworzyliśmy repozytorium Gita, które zawiera plik o powyższej zawartości. Następnie wpisujemy: + + $ git checkout -b szef # wydaje się, jakby nic się nie stało + $ echo "Mój szef jest ode mnie mądrzejszy" > mój_plik.txt + $ git commit -a -m "Druga wersja" + +Wygląda jakbyśmy zmienili zawartość pliku i wykonali 'commit'. Ale to iluzja. Wpisz: + + $ git checkout master # przejdź do oryginalnej wersji + +i hokus-pokus! Poprzedni plik jest przywrócony do stanu pierwotnego. Gdyby jednak szef zdecydował się grzebać w twoim katalogu, wpisz: + + $ git checkout szef # przejdź do wersji, która nadaje się do obejrzenia przez szefa + +Możesz zmieniać pomiędzy tymi wersjami pliku tak często jak zechcesz, każdą z tych wersji pliku możesz też niezależnie redagować. + +=== Brudna robota === + +[[branch]] +Załóżmy, pracujesz nad jakąś funkcją, i musisz z jakiegokolwiek powodu, wrócić o 3 wersje wstecz w celu wprowadzenia kilku poleceń print, aby sprawdzić jej działanie. Wtedy: + + $ git commit -a + $ git checkout HEAD~3 + +Teraz możesz na dziko wprowadzać tymczasowy kod. Możesz te zmiany nawet 'commit'. Po skończeniu, + + $ git checkout master + +wróci cię do poprzedniej pracy. Zauważ, że wszystkie zmiany, które nie zostały zatwierdzone przez 'commit', zostały przejęte. + +A co jeśli chciałeś zapamiętać wprowadzone zmiany? Proste: + + $ git checkout -b brudy + +i tylko jeszcze wykonaj 'commit' zanim wrócisz do master branch. Jeśli tylko chcesz wrócić do twojej brudnej roboty, wpisz po prostu + + $ git checkout brudy + +Spotkaliśmy się z tym poleceniem już we wcześniejszym rozdziale, gdy poruszaliśmy temat ładowania starych wersji. Teraz możemy opowiedzieć cala prawdę: pliki zmieniają się do żądanej wersji, jednak musimy opuścić master branch. Każdy 'commit' od teraz prowadzi twoje dane inna droga, której możemy również nadać nazwę. + +Innymi słowami, po przywołaniu ('checkout') starszego stanu Git automatycznie przenosi cię do nowego, nienazwanego brunch, który poleceniem *git checkout -b* otrzyma nazwę i zostanie zapamiętany. + +=== Szybka korekta błędów === + +Będąc w środku jakiejś pracy, otrzymujesz polecenie zajęcia się nowo znaleziono błędem w 'commit' `1b6d...`: + + $ git commit -a + $ git checkout -b fixes 1b6d + +Po skorygowaniu błędu: + + $ git commit -a -m "Błąd usunięty" + $ git checkout master + +i kontynuujesz przerwana prace. Możesz nawet ostatnio świeżo upieczona poprawkę przejąć do aktualnej wersji: + + $ git merge fixes + +=== Merge === + +Za pomocą niektórych systemów kontroli wersji utworzenie nowego 'branch' może i jest proste, jednak późniejsze połączenie ('merge') skomplikowane. Z Gitem 'merge' jest tak trywialne, że możesz czasem nawet nie zostać powiadomiony o jego wykonaniu. + +W gruncie rzeczy spotkaliśmy się już wiele wcześniej z funkcją 'merge'. Polecenie *pull* ściąga inne wersje i je zespala ('merge') z twoim aktualnym 'branch'. Jeśli nie wprowadziłeś żadnych lokalnych zmian, to 'merge' jest szybkim przejściem do przodu, jest to przypadek podobny do zładowania ostatniej wersji przy centralnych systemach kontroli wersji. Jeśli jednak wprowadziłeś zmiany, Git automatycznie wykona 'merge' i powiadomi cie o ewentualnych konfliktach. + +Zwyczajnie każdy 'commit' posiada matczyny 'commit', a mianowicie poprzedzający go 'commit'. Zespolenie kilku 'branch' wytwarza 'commit' z minimum 2 matczynymi 'commit'. To nasuwa pytanie, który właściwie'commit' wskazuje na `HEAD~10`? Każdy 'commit' może posiadać więcej rodziców, za którym właściwie podążamy? + +Wychodzi na to, ze ta notacja zawsze wybiera pierwszego rodzica. To jest wskazane, ponieważ aktualny 'branch' staje się pierwszym rodzicem podczas 'merge', częściej będziesz zainteresowany bardziej zmianami których dokonałeś w aktualnym 'branch', niż w innych. + +Możesz tez wybranego rodzica wskazać używając symbolu dzióbka. By na przykład pokazać logi drugiego rodzica. + + $ git log HEAD^2 + +Możesz pominąć numer pierwszego rodzica. By na przykład pokazać różnice z pierwszym rodzicem: + + $ git diff HEAD^ + +Możesz ta notacje kombinować także z innymi rodzajami. Na przykład: + + $ git checkout 1b6d^^2~10 -b archaiczne + +tworzy nowy 'branch' o nazwie '``archaiczne', reprezentujący stan 10 'commit' do tyłu drugiego rodzica dla pierwszego rodzica 'commit', którego hash rozpoczyna się na 1b6d. + +=== Bez przestojowy przebieg pracy === + +W procesie produkcji często drugi krok planu musi czekać na zakończenie pierwszego. Popsuty samochód stoi w garażu nieużywany do czasu dostarczenia części zamiennej. Prototyp musi czekać na wyprodukowanie jakiegoś chipa zanim będzie można podjąć dalsza konstrukcje. + +W projektach software może to wyglądać podobnie. Druga część jakiegoś feature musi czekać, aż pierwsza zostanie wydana i przetestowana. Niektóre projekty wymagają sprawdzenia twojego kodu zanim zostanie zaakceptowany, musisz wiec czekać, zanim pierwsza część zostanie sprawdzona, zanim będziesz mógł zacząć z druga częścią + +Dzięki bezbolesnemu 'branch' i 'merge' możemy te reguły naciągnąć i pracować nad druga częścią jeszcze zanim pierwsza zostanie oficjalnie zatwierdzona. Przyjmijmy, że wykonałeś 'commit' pierwszej części i przekazałeś do sprawdzenia. Przyjmijmy też, że znajdujesz się w 'master branch'. Najpierw przejdź do 'branch'o nazwie część2: + +$ git checkout -b część2 + +Pracujesz w części 2 i regularnie wykonujesz 'commit'. Błądzenie jest ludzkie i może się zdarzyć, że chcecie wrócić do części 1 i wprowadzić jakieś poprawki. Jeśli masz szczęście albo jesteś bardzo dobry, możesz ominąć następne linijki. + + $ git checkout master # przejdź do części 1 + $ fix_problem + $ git commit -a # zapisz rozwiązanie + $ git checkout część2 # przejdź do części 2 + $ git merge master # połącz zmiany + +Ewentualnie, część pierwsza zostaje dopuszczona: + + $ git checkout master # przejdź do części 1 + $ submit files # opublikuj twoja wersję + $ git merge część2 # Połącz z częścią 2 + $ git branch -d część2 # usuń branch część2 + +Znajdujesz się teraz z powrotem w master branch posiadając część2 w katalogu roboczym. + +Dość łatwo zastosować ten sam trik na dowolną ilość części. Równie łatwo można utworzyć 'branch' wstecznie: przypuśćmy, właśnie spostrzegłaś, iż już właściwie jakieś 7 'commit' wcześniej powinnaś stworzyć 'branch'. Wpisz wtedy: + + $ git branch -m master część2 # Zmień nazwę "master" na "część2". + $ git branch master HEAD~7 # utwórz ponownie "master" 7 'commits' do tyłu. + +Teraz `master` branch zawiera cześć 1 a branch `część2` zawiera całą resztę. Znajdujemy się teraz w tym ostatnim 'branch'; utworzyliśmy `master` bez wchodzenia do niego, gdyż zamierzamy dalszą pracę prowadzić w 'branch' `część2`. Nie jest to zbyt często stosowane. Do tej pory przechodziliśmy do nowego 'branch' zaraz po jego utworzeniu, tak jak w: + + $ git checkout HEAD~7 -b master # Utwórz branch i wejdź do niego. + +=== Reorganizacja składanki === + +Może lubisz odpracować wszystkie aspekty projektu w jednym 'branch'. Chcesz wszystkie bieżące zmiany zachować dla siebie, a wszyscy inni powinni zobaczyć twoje 'commit' po ich starannym zorganizowaniu. Wystartuj parę 'branch': + + $ git branch czyste # Utwórz branch dla oczyszczonych 'commits'. + $ git checkout -b zbieranina # utwórz 'branch' i przejdź do niego w celu dalszej pracy. + +Następnie wykonaj zamierzone prace: pousuwaj błędy, dodaj nowe funkcje, utwóż kod tymczasowy i tak dalej, regularnie wykonując 'commit'. Wtedy: + + $ git checkout czyste + $ git cherry-pick zbieranina^^ + +zastosuje najstarszy matczyny 'commit' z 'branch' ``zbieranina'' na 'branch' ``czyste''. Poprzez 'przebranie wisienek' możesz tak skonstruować 'branch', który posiada jedynie końcowy kod i zależne od niego pogrupowane 'commit'. + +=== Zarządzanie 'branch' === + +Listę wszystkich 'branch' otrzymasz poprzez: + + $ git branch + +Standardowo zaczynasz w 'branch' zwanym ``master''. Wielu opowiada się za pozostawieniem ``master'' branch w stanie dziewiczym i tworzeniu nowych dla twoich prac. + +Opcje *-d* und *-m* pozwalają na usuwanie i przesuwanie (zmianę nazwy) 'branch'. Zobacz: *git help branch*. + +Nazwa ``master'' jest bardzo użytecznym stworem. Inni mogą wychodzić z założenia, że twoje repozytorium takowy posiada i że zawiera on oficjalną wersję projektu. Nawet jeśli mógłbyś skasować lub zmienić nazwę na inną powinnaś respektować tę konwencję. + +=== Tymczasowe 'branch' === + +Po jakimś czasie zapewne zauważysz, że często tworzysz 'branch' o krótkiej żywotności, w większości z tego samego powodu: każdy nowy 'branch' służy jedynie do tego, by zabezpieczyć aktualny stan,, aby móc wrócić do jednego z poprzednich punktów i poprawić jakieś priorytetowe błędy czy cokolwiek innego. + +Można to porównać do chwilowego przełączenia kanału telewizyjnego, by sprawdzić co dzieje się na innym. Lecz zamiast naciskać guziki pilota, korzystasz z poleceń 'create', 'checkout', 'merge' i 'delete'. Na szczęście Git posiada na te operacje skrót, który jest tak samo komfortowy jak pilot telewizora: + + $ git stash + +Polecenie to zabezpiecza aktualny stan w tymczasowym miejscu ('stash' = ukryj) i przywraca poprzedni stan. Twój katalog roboczy wygląda dokładnie tak, jak wyglądał zanim zacząłeś edycję. Teraz możesz poprawiać błędy, zładować zmiany z centralnego repozytorium ('pull') i tak dalej. Jeśli chcesz powrócić z powrotem do ukrytego ('stashed') stanu, wpisz: + + $ git stash apply # Prawdopodobnie będziesz musiał rozwiązać konflikty. + +Możesz posiadać więcej 'stash'-ów i traktować je w zupełnie inny sposób. Zobacz *git help stash*. Jak już prawdopodobnie się domyślasz, Git korzysta przy wykonywaniu tej magicznej sztuczki z funkcji 'branch' w tle. + +=== Pracuj jak chcesz === + +Może pytasz się, czy 'branch' są warte tego zachodu. Jakby nie było, polecenia 'clone' są prawie tak samo szybkie i możesz po prostu poleceniem *cd* zmieniać pomiędzy nimi, bez stosowania ezoterycznych poleceń Gita. + +Przyjrzyjmy się takiej przeglądarce internetowej. Dlaczego pozwala używać tabów tak samo jak i nowych okien? Ponieważ udostępnienie obu możliwości pozwala na stosowanie wielu stylów. Niektórzy użytkownicy preferują otwarcie jednego okna przeglądarki i korzystają z tabów dla wyświetlenia różnych stron. Inni upierają się przy stosowaniu pojedynczych okien dla każdej strony,zupełnie bez korzystania z tabów. Jeszcze inni znowu wolą coś pomiędzy. + +Stosowanie 'branch' to jak korzystanie tabów dla twojego katalogu roboczego, a klonowanie porównać można do otwarcia wielu okien przeglądarki. Obie operacje są szybkie i lokalne, dlaczego nie poeksperymentować i nie znaleźć dla siebie najbardziej odpowiedniej kombinacji. Git pozwoli ci pracować dokładnie tak jak chcesz. diff --git a/szl/clone.txt b/szl/clone.txt new file mode 100644 index 0000000..27b92a7 --- /dev/null +++ b/szl/clone.txt @@ -0,0 +1,194 @@ +== Klonowanie == + +W starszych systemach kontroli wersji polecenie 'checkout' stanowi standardową operacje pozyskiwania danych. Otrzymasz nią zbiór plików konkretnej wersji. + +W Git i innych rozproszonych systemach standardowo służy temu operacja 'clone'. By pozyskać dane, tworzysz najpierw klon całego repozytorium. Lub inaczej mówiąc, otrzymujesz lustrzane odbicie serwera. Wszystko, co można zrobić w centralnym repozytorium, możesz również robić z klonem. + +=== Synchronizacja komputera === + +W celu ochrony danych mógłbym jeszcze zaakceptować korzystanie z archiwum 'tar', a dla prostej synchronizacji używania *rsync*. Jednak czasami pracując na laptopie, a innym razem na komputerze stacjonarnym, może się zdarzyć, że komputery nie miały możliwości zsynchwonizowania się w międzyczasie. + +Utwórz repozytorium Gita w wykonaj 'commit' twoich danych na jednym z komputerów. Potem na następnym wpisz: + + $ git clone drugi.komputer:/ścieżka/do/danych + +by otrzymać drugą kopie danych i jednocześnie utworzyć repozytorium Gita na drugim komputerze. Od teraz poleceniem: + + $ git commit -a + $ git pull drugi.komputer:/ścieżka/do/danych HEAD + +przenosisz stan drugiego komputera na komputer na którym właśnie pracujesz. Jeśli dokonałeś zmian w tym samym pliku na obydwu komputerach, Git poinformuje cię o tym, po usunięciu konfliktu powinieneś ponowić 'commit'. + +=== Klasyczna kontrola kodu źródłowego === + +Utwórz repozytorium Gita w katalogu roboczym projektu: + + $ git init + $ git add . + $ git commit -m "Pierwszy commit" + +Na centralnym serwerze utwórz gołe ('bare') repozytorium w jakimkolwiek katalogu: + + $ mkdir proj.git + $ cd proj.git + $ git --bare init + $ touch proj.git/git-daemon-export-ok + +W razie konieczności, wystartuj daemon Gita: + + $ git daemon --detach # ponieważ, gdyby uruchomiony był wcześniej + +Jeśli korzystasz z hostingu to poszukaj na stronie usługodawcy wskazówek jak utworzyć repozytorium 'bare'. Zwykle konieczne jest do tego wypełnienie formularza online na jego stronie. + +Popchaj ('push') twój projekt teraz na centralny serwer: + + $ git push centralny.serwer/ścieżka/do/projektu.git HEAD + +By pozyskać kod źródłowy programista podaje zwykle polecenie w rodzaju: + + $ git clone główny.serwer/ścieżka/do/projektu.git + +Po dokonaniu edycji programista zapamiętuje zmiany najpierw lokalnie: + + $ git commit -a + +Aby zaktualizować do wersji istniejącej na głównym serwerze: + + $ git pull + +Jeśli wystąpią jakiekolwiek konflikty łączenia ('merge') , powinny być najpierw usunięte i na nowo zostać wykonany 'commit'. + + $ git commit -a + +Lokalne zmiany przekazujemy do serwera poleceniem: + + $ git push + +Jeśli w międzyczasie nastąpiły nowe zmiany na serwerze wprowadzone przez innego programistę, twój 'push' nie powiedzie się. Zaktualizuj lokalne repozytorium ponownie poleceniem 'pull', pozbądź się konfliktów i spróbuj jeszcze raz. + +Programiści potrzebują dostępu poprzez SSH dla wykonania poleceń 'pull' i 'push'. Mimo to, każdy może skopiowć kod źródłowy poprzez podanie: + + $ git clone git://główny.serwer/ścieżka/do/projektu.git + +Protokół GIT przypomina HTTP: nie posiada autentyfikacji, a więc każdy może skopiować dane. Przy ustawieniach standardowych wykonanie 'push' za pomocą protokołu GIT jest zdezaktywowane. + +=== Utajnienie źródła === + +Przy projektach Closed-Source wyklucz używanie poleceń takich jak 'clone' i upewnij się, że nie został utworzony plik o nazwie 'git-daemon-export-ok'. Wtedy repozytorium nie może już komunikować się poprzez protokół 'git', tylko posiadający dostęp przez SHH mogą widzieć dane. Jeśli wszystkie repozytoria są zamknięte, nie ma potrzeby startować daemona 'git', ponieważ cała komunikacja odbywa się wyłącznie za pomocą SSH. + +=== Gołe repozytoria === + +Określenie: 'gołe' ('bare') repozytorium, powstało, ze względu na fakt, iż repozytorium to nie posiada katalogu roboczego. Posiada jedynie dane, które są zwykle schowane w podkatalogu '.git'. Innymi słowy: zarządza historią projektu, nie posiada jednak katalogu roboczego jakiejkolwiek wersji. + +Repozytorium 'bare' przejmuje rolę podobną do roli głównego serwera w scentralizowanych systemach kontroli wersji: dach twojego projektu. Programiści klonują twój projekt stamtąd i przesyłają tam ostatnie oficjalne zmiany. Często znajduje się ono na serwerze, którego jedynym zadaniem jest wyłącznie dystrybucja danych. Sama praca nad projektem przebiega na jego klonach, w ten sposób główne repozytorium daje sobie radę nie korzystając z katalogu roboczego. + +Wiele z poleceń Gita nie będzie funkcjonować w repozytoriach 'bare', chyba że ustawimy zmienną systemową GIT_DIR na katalog roboczy repozytorium albo przekażemy opcję `--bare`. + +=== 'Push', czy 'pull'? === + +Dlaczego wprowadziliśmy polecenie 'push', a nie pozostaliśmy przy znanym nam już 'pull'? Po pierwsze, 'pull' nie działa z repozytoriami 'bare': zamiast niego używaj 'fetch', polecenia którym zajmiemy się później. Również gdybyśmy nawet używali normalnego repozytorium na serwerze centralnym, polecenie 'pull' byłoby raczej niewygodne. Musielibyśmy wpierw zalogować się na serwerze i przekazać poleceniu 'pull' adres IP komputera z którego chcemy ściągnąć pliki. Jeśli w ogóle posiadalibyśmy dostęp do konsoli serwera, to prawdopodobnie przeszkodziłaby nam firewalle na drodze do naszego komputera. + +W każdym bądź razie, nawet jeśli nawet mogło by to zadziałać, odradzam z korzystania z funkcji 'push' w ten sposób. Poprzez samo posiadanie katalogu roboczego na serwerze mogłoby powstać wiele nieścisłości. + +Krótko mówiąc, podczas gdy uczysz się korzystania z Git, korzystaj z polecenia 'push' tylko, gdy celem jest repozytorium 'bare', w wszystkich innych wypadkach z 'pull'. + +=== Rozwidlenie projektu === + +Jeśli nie potrafisz już patrzeć na kierunek rozwoju w jakim poszedł jakiś projekt. Uważasz, że potrafisz to lepiej. To po prostu utwórz jego 'frk', w tym celu na twoim serwerze podaj: + + $ git clone git://główny.serwer/ścieżka/do/danych + +Następnie, poinformuj wszystkich o nowym 'forku' projektu na twoim serwerze. + +W każdej późniejszej chwili możesz dokonać łączenia ('merge') zmian z oryginalnego projektu, poprzez: + + $ git pull + +=== Ultymatywny backup danych === + +Chcesz posiadać liczne, wolne od manipulacji, redundantne kopie bezpieczeństwa w różnych miejscach? Jeśli projekt posiada wielu programistów, nie musisz niczego robić. Ponieważ każdy klon twojego kodu jest pełnowartościową kopią bezpieczeństwa. Nie tylko jego aktualny stan, lecz również cała historia projektu. Gdy jakikolwiek klon zostanie uszkodzony, dzięki kryptograficznemu hashowaniu, zostanie to natychmiast rozpoznane, jeśli tylko ta osoba będzie próbować wymiany z innymi. + +Jeśli twój projekt nie jest jeszcze wystarczająco znany, spróbuj pozyskać tak wiele serwerów, ile to możliwe, by umieścić tam jego klon. + +Ci najbardziej paranoidalni powinni zawsze zapisywać 20 bajtów ostatniej sumy kontrolnej SHA1 dla HEAD i przechowywać w bezpiecznym miejscu. Musi być bezpieczny, jednak nie tajny. Na przykład opublikowanie go w gazecie dobrze by spełniło swoje zadanie, dość trudnym zadaniem byłoby zmanipulowanie każdej kopii gazety. + +=== Wielozadaniowość z prędkością światła === + +Załóżmy, że chcesz pracować nad kilkoma funkcjami równocześnie. Wykonaj 'commit' i wpisz: + + $ git clone . /jakiś/nowy/katalog + +Za sprawą http://en.wikipedia.org/wiki/Hard_link[twardych linków] stworzenie lokalnej kopii zajmuje dużo mniej czasu i pamięci niż zwykły backup. + +Możesz pracować nad dwoma niezależnymi funkcjami jednocześnie. Na przykład, możesz pracować nad jednym klonem dalej, podczas gdy drugi jest właśnie kompilowany. W każdym momencie możesz wykonać 'commit' i 'pull' innego klonu. + + $ git pull /inny/klon HEAD + +=== Kontrola wersji z podziemia === + +Pracujesz nad projektem, który używa innego systemu kontroli wersji i tęsknisz za Gitem? Utwórz po prostu repozytorium Gita w twoim katalogu roboczym: + + $ git init + $ git add . + $ git commit -m "Pierwszy commit" + +następnie sklonuj go: + + $ git clone . /jakiś/inny/katalog + +Przejdź teraz do nowego katalogu i pracuj według upodobania. Kiedyś zechcesz zsynchronizować pracę, idź do oryginalnego katalogu, zaktualizuj go najpierw z tym innym systemem kontroli wersji, następnie wpisz: + + $ git add . + $ git add . $ git commit -m"Synchronizacja z innym systemem kontroli wersji" + +Teraz przejdź do nowego katalogu i podaj: + + $ git commit -a -m "Opis zmian" + $ git pull + +Sposób w jaki przekażesz zmiany drugiemu systemowi zależy już od jego sposobu działania. Twój nowy katalog posiada dane ze zmianami przez ciebie wprowadzonymi. Wykonaj jeszcze adekwatne kroki żądane przez ten inny system kontroli wersji, by przekazać dane do centralnego składu. + +Subversion, być może najlepszy z centralnych systemów, stosowany jest w wielu projektach. Komenda *git svn* automatyzuje powyższe kroki, może być użyta na przykład do http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[exportu projektu Git do repozytorium Subversion]. + +=== Mercurial === + +Mercurial to podobny do Gita system kontroli wersji, który prawie bezproblemowo potrafi pracować z Gitem. Korzystając z rozszerzenia `hg-git` użytkownik Mercurial jest w stanie prawie bez strat wykonywać 'push' i 'pull' z repozytorium Gita. + +Ściągnij sobie rozszerzenie `hg-git` za pomocą Gita: + + $ git clone git://github.com/schacon/hg-git.git + +albo za pomocą Mercurial: + + $ hg clone http://bitbucket.org/durin42/hg-git/ + +Niestety nie są mi znane takie rozszerzenia dla Gita. Dlatego jestem za używaniem Gita jako głuwnego repozytorium, nawet gdy preferujesz Mercurial. W projektach prowadzonych za pomocą Mercurial często znajdziemy woluntariusza, który równolegle prowadzi repozytorium Gita, do którego dzięki pomocy rozszerzenia `hg-git` projekty Gita automatycznie osiągają użytkowników Mercurial. + +To rozszerzenie potrafi również zmienić skład Mercurial w skład Gita, za pomocą komendy 'push' do gołego repozytorium Gita. Jeszcze łatwiej dokonamy tego skryptem `hg-fast-export.sh`, który możemy tu znaleźć: + + $ git clone git://repo.or.cz/fast-export.git + +Aby skonwertować, wejdź do pustego katalogu: + + $ git init + $ hg-fast-export.sh -r /hg/repo + +po uprzednim dodaniu skryptu do twojego `$ PATH`. + +=== Bazaar === + +Wspomnijmy również pokrótce o Bazaar, ponieważ jest to najbardziej popularny darmowy rozproszony system kontroli wersji po Git i Mercurial. + +Bazar będąc stosunkowo młodym systemem, posiada zaletę perspektywy czasu, jego twórcy mogli uczyć się na błędach z przeszłości i uniknąć historycznych naleciałości. Poza tym programiści byli świadomi popularności i wagi interakcji z innymi systemami kontroli wersji. + +Rozszerzenie `Bzr-git` pozwala użytkownikom Bazar dość łatwo pracować z repozytoriami Gita. Program `tailor` konwertuje składy Bazaar do składów Git i może robić to na bieżąco, podczas gdy `bzr-fast-export` lepiej nadaje się do jednorazowej konwersji. + +=== Dlaczego korzystam z Git === + +Zdecydowałem się pierwotnie do wyboru GIT, ponieważ słyszałem, że jest w stanie zarządzać tak zawiłym i rozległym projektem jak kod źródłowy Linuksa. Jak na razie nie miałem powodów do zmiany. Git służył mi znakomicie i jak na razie jeszcze mnie nie zawiódł. Ponieważ w pierwszej linii pracuje na Linuksie, problemy innych platform nie mają dla mnie znaczenia. + +Preferuję również programy 'C' i skrypty 'bash' w opozycji do na przykład Pythona: posiadają mniej zależności, i wolę też, gdy kod jest wykonywany szybko. + +Myślałem już też nad tym, jak można by ulepszyć Gita, poszło to nawet tak daleko, że napisałem własną aplikacje podobną do niego, w celu jednak wyłącznie ćwiczeń akademickich. Nawet gdybym zakończył mój projekt, mimo to pozostałbym przy Git, bo ulepszenia byłyby zbyt minimalne by uzasadnić zastosowanie odosobnionego systemu. + +Oczywiście może się okazać, że twoje potrzeby i oczekiwania są zupełnie inne i być może wygodniej jest tobie z zupełnie innym systemem. Jakby jednak nie spojrzeć, stosując Git nie popełnisz nic złego. diff --git a/szl/drawbacks.txt b/szl/drawbacks.txt new file mode 100644 index 0000000..5785cf0 --- /dev/null +++ b/szl/drawbacks.txt @@ -0,0 +1,91 @@ +== Załącznik A: Niedociągnięcia Gita == + +O kilku problemach mogących wystąpić z GIT nie wspomniałem do tej pory. Niektóre z nich można łatwo rozwiązać korzystając ze skryptów i 'hooks', inne wymagają reorganizacji i ponownego zdefiniowania całego projektu, a na rozwiązanie kilku innych uniedogodnień możesz tylko uzbroić się w cierpliwość i czekać na ich rozwiązanie. Albo jeszcze lepiej, samemu się nimi zająć i spróbować pomóc. + +=== Słabości SHA1 === + +Z biegiem czasu kryptografowie odkrywają coraz więcej słabości systemu SHA1. Już dzisiaj byłoby możliwe dla przedsiębiorstw dysponujących odpowiednimi zasobami finansowymi znaleźć kolizje w hashach Za kilka lat możliwe, że całkiem normalny domowy PC będzie dysponował odpowiednim zasobem mocy obliczeniowej, by skorumpować niepostrzeżenie repozytorium Gita. + +Miejmy nadzieję, że Git przestawi się na lepszą funkcje hashującą, zanim badania nad SHA1 zupełnie zrobią go bezużytecznym. + +=== Microsoft Windows === + +Korzystanie z Gita pod Microsoft Windows może być frustrujące: + +- http://cygwin.com/[Cygwin], unixoidalne środowisko dla Windowsa posiada http://cygwin.com/packages/git/[port Gita]. + +- http://code.google.com/p/msysgit/[Git dla MSys] jest jedną z alternatyw, niektóre polecenia wymagają jednak poprawek. + +=== Pliki niepowiązane === + +Jeśli twój projekt jest bardzo duży i zawiera wiele plików, które nie są bezpośrednio ze sobą związane, mimo to jednak często zostają zmieniane, Git może tu działać gorzej niż inne systemy, ponieważ nie prowadzi monitoringu poszczególnych plików. Git kontroluje zawsze całość projektu, co w normalnym wypadku jest zaletą. + +Jednym z możliwych rozwiązań mogłoby być podzielenie twojego projektu na kilka mniejszych, w których znajdują się jedynie pliki od siebie zależne. Korzystaj z *git submodule* jeśli mimo to chcesz cały twój projekt mieć w tym samym repozytorium. + +=== Kto nad czym pracuje? === + +Niektóre systemy kontroli wersji zmuszają cię, by w jakiś sposób oznaczyć pliki nad którymi pracujesz. Mimo że jest to bardzo uciążliwe, gdyż wymaga ciągłej komunikacji z serwerem centralnym, posiada to też swoje zalety: + +1. Różnice zostają szybko znalezione, ponieważ wystarczy skontrolować wyłącznie oznaczone pliki. + +2. Każdy może szybko sprawdzić, kto aktualnie nad czym pracuje, sprawdzając na serwerze po prostu kto zaznaczył jakie dane do edycji. + +Używając odpowiednich skryptów uda ci się to również przy pomocy Gita. Wymaga to jednak współdziałania programistów, ponieważ muszą również korzystać z tych skryptów podczas pracy nad plikiem. + +=== Historia pliku === + +Ponieważ Git loguje zmiany tylko dla całości projektu jako takiego, rekonstrukcja przebiegu zmian pojedynczego pliku jest bardziej pracochłonna, niż w innych systemach, które kontrolują pojedyncze pliki. + +Te wady są w większości przypadków marginalne i nie są brane pod uwagę, ponieważ inne operacje są bardzo wydajne. Na przykład polecenie `git checkout` jest szybsze niż `cp -a`, zmiany w zakresie całego projektu daje się lepiej komprymować niż zbiór zmian na bazie pojedynczych plików. + +=== Pierwszy klon === + +Wykonanie klonu jest kosztowniejsze niż w innych systemach kontroli wersji jeśli istnieje długa historia. + +Początkowy koszt spłaca się jednak na dłuższą metę ponieważ większość przyszłych operacji przeprowadzane będzie szybko i offline. Niemniej jednak istnieją sytuacje, w których lepiej utworzyć powierzchowny klon korzystając z opcji `--depth`. Trwa to o wiele krócej, taki klon taki posiada też tylko ograniczoną funkcjonalność. + +=== Niestałe projekty === + +Git został napisany z myślą optymalizacji prędkości działania przy dokonywaniu wielkich zmian. Ludzie robią jednak pomniejsze zmiany z wersji na wersję. Jakaś poprawka tutaj, jakaś nowa funkcja gdzie indziej, poprawienie komentarzy itd. Ale jeśli twoje dane znacznie się od siebie różnią pomiędzy następującymi po sobie wersjami, to chcąc nie chcąc przy każdym 'commit' projekt zwiększy się o twoje zmiany. + +Nie wymyślono jednak do tej pory niczego w żadnym systemie kontroli wersji, by móc temu zapobiec, tutaj jednak użytkownik Gita cierpi najbardziej, ponieważ w normalnym wypadku klonuje cały przebieg projektu. + +Powinno się w takim wypadku szukać powodów wystąpienia największych zmian. Ewentualnie można czasami zmienić format danych. Małe zmiany w projekcie powinny pociągać tylko minimalne zmiany na tak wąskiej grupie plików, jak to tylko możliwe. + +Może czasami bardziej wskazana byłaby baza danych, czy jakiś system archiwizacji zamiast systemu kontroli wersji. Na przykład nie jest dobrym sposobem zastosowanie systemu kontroli wersji do zarządzania zdjęciami wykonywanymi periodycznie przez kamerę internetową. + +Jeśli dane ulegają ciągłym zmianom i naprawdę muszą być objęte kontrolą wersji, jedną z możliwości jest zastosowanie Git w scentralizowanej formie. Każdy może dokonywać pobieżnych klonów, które mało co lub wcale nie mają nic do czynienia z przebiegiem projektu. Oczywiście w takim wypadku wiele funkcji Gita nie będzie dostępnych a zmiany muszą być przekazywane w formie 'patch'. Prawdopodobnie będzie to dość dobrze działać, mimo iż nie jest do końca jasne komu potrzebna jest znajomość przebiegu tak ogromnej ilości niestabilnych danych. + +Innym przykładem może być projekt, który zależny jest od firmware przyjmującej kształt wielkiej danej w formie binarnej. Historia pliku firmware nie interesuje użytkownika, a zmiany nie pozwalają się wygodnie komprymować, wielkość repozytorium wzrasta niepotrzebnie o nowe wersje binarnego pliku firmware. + +W takim wypadku należałoby trzymać w repozytorium wyłącznie kod źródłowy, a sam plik binarny poza nim. By ułatwić sobie życie, ktoś mógłby opracować skrypt, który Git wykorzystuje do klonowania kodu źródłowego i 'rsync' albo pobieżny klon dla samego firmware. + +=== Licznik globalny === + +Wiele systemów kontroli wersji udostępnia licznik, który jest zwiększany z każdym "commit". Git natomiast odwołuje się przy zmianach do klucza SHA1, który w wielu przypadkach jest lepszym rozwiązaniem. + +Niektórzy jednak przyzwyczaili się do tego licznika. Na szczęście, łatwo jest pisać skrypty, zwiększające stan licznika przy każdej aktualizacji centralnego repozytorium Gita. Może jako forma taga, który powiązany jest z kluczem SHA1 ostatniego 'commit'. + +Każdy klon mógłby posiadać taki licznik, jednak byłby on prawdopodobnie bezużyteczny, ponieważ tylko licznik centralnego repozytoriom ma znaczenie. + +=== Puste katalogi === + +Nie ma możliwości kontroli wersji pustych katalogów. Aby obejść ten problem wystarczy utworzyć w takim katalogu plik dummy. + +To raczej obecna implementacja Gita, a mniej jego konstrukcja, jest odpowiedzialna za to wadę. Przy odrobinie szczęścia, jeśli Git jeszcze bardziej się upowszechni i więcej użytkowników żądać będzie tej funkcji, to być może zostanie zaimplementowana. + +=== Pierwszy 'commit' === + +Stereotypowy informatyk liczy od 0 zamiast 1. Niestety, w kwestii 'commits' Git nie podąża za tą konwencją. Wiele komend marudzi przed wykonaniem pierwszego 'commit'. Dodatkowo, różnego rodzaju krańcowe przypadki muszą być traktowane specjalnie, jak 'rebase' dla 'branch' o różniącym się pierwszym 'commit'. + +Git zyskałby na zdefiniowaniu tzw. 'zero - commit', ponieważ zaraz po zainicjowaniu repozytorium, 'HEAD' otrzymałby 20 bajtowy klucz SHA1. Ten specjalny 'commit' reprezentowałby puste drzewo, bez rodziców, być może pradziad wszystkich repozytoriów. + +Jeśli na przykład użytkownik wykonałby polecenie *git log*, zostałby poinformowany, że nie istnieje jeszcze żaden 'commit', gdzie na dzień dzisiejszy taka komenda wywoła błąd. Analogicznie dzieje się też z innymi poleceniami. + +Każdy inicjujący 'commit' byłby pochodną tego zerowego 'commit'. + +Niestety występuje jeszcze kilka innych problemów. Jeśli chcemy scalić kilka 'branches' o różniących sie inicjalnych 'commits' i przeprowadzić 'rebase', musimy ręcznie ingerować. + +=== Charakterystyka zastosowania === + +Dla 'commits' A i B, znaczenie wyrażeń "A..B" i "A...B" zależy od tego, czy polecenie oczekuje dwóch punktów końcowych, czy zakresu. Sprawdź *git help diff* i *git help rev-parse*. diff --git a/szl/grandmaster.txt b/szl/grandmaster.txt new file mode 100644 index 0000000..32860b4 --- /dev/null +++ b/szl/grandmaster.txt @@ -0,0 +1,179 @@ +== Git dla zaawansowanych == + +W międzyczasie powinnaś umieć odnaleźć się na stronach *git help* i rozumieć większość zagadnień. Mimo to może okazać się dość mozolne odnalezienie odpowiedniej komendy dla rozwiązania pewnych zadań. Może uda mi się zaoszczędzić ci trochę czasu: poniżej znajdziesz kilka recept, które były mi przydatne w przeszłości. + +=== Publikowanie kodu źródłowego === + +Git zarządza w moich projektach dokładnie tymi danymi, które chcę archiwizować i dać do dyspozycji innym użytkownikom. Aby utworzyć archiwum tar kodu źródłowego, używam polecenia + + $ git archive --format=tar --prefix=proj-1.2.3/ HEAD + +=== Zmiany dla 'commit' === + +Powiadomienie Gita o dodaniu, skasowaniu czy zmianie nazwy plików może okazać sie przy niektórych projektach dość uciążliwą pracą. Zamiast tego można skorzystać z: + + $ git add . + $ git add -u + +Git przyjży się danym w aktualnym katalogu i odpracuje sam szczegóły. Zamiast tego drugiego polecenia możemy użyć `git commit -a`, jeśli i tak mamy zamiar przeprowadzić 'comit'. Sprawdź też *git help ignore*, by dowiedzieć się jak zdefiniować dane, które powinny być zignorowane. + +Można to także wykonać za jednyym zamachem: + + $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove + +Opcje *-z* i *-0* zapobiegą przed niechcianymi efektmi ubocznymi przez niestandardowe znaki w nazwach plików. Ale ponieważ to polecenie dodaje również pliki które powinny być zignorowane, można dodać do niego jeszcze opcje `-x` albo `-X` + +=== Mój 'commit' jest za duży! === + +Od dłuższego czasu nie pamiętałeś o wykonaniu 'commit'? Tak namiętnie programowałeś, że zupełnie zapomniałeś o kontroli kodu źródłowego? Przeprowadzasz serię niezależnych zmian, bo jest to w twoim stylu? + +Nie ma sprawy, wpisz polecenie: + + $ git add -p + +Dla każdej zmiany, której dokonałeś Git pokaże ci pasaże z kodem, który uległ zmianom i spyta cię, czy mają zostać częścią następnego 'commit'. Odpowiedz po prostu "y" dla tak, albo "n" dla nie. Dysponujesz jeszcze innymi opcjami, na przykład dla odłożenie w czasie decyzji, wpisz "?", by dowiedzieć się więcej. + +Jeśli jesteś już zadowolony z wyniku, wpisz: + + $ git commit + +by dokonać wybranych zmian. Uważaj tylko, by nie skorzystać z opcji *-a*, ponieważ wtedy git dokona 'commit' zawierający wszystkie zmiany. + +A co, jeśli pracowałeś nad wieloma danymi w wielu różnych miejscach? Sprawdzenie każdej danej z osobna jest frustrujące i męczące zarówno. W takim wypadku skorzystaj z *git add -i*, obsługa tego polecenia może nie jest zbyt łatwa, za to jednak bardzo elastyczna. Kilkoma naciśnięciami klawiszy możesz wiele zmienionych plików dodać ('stage') albo usunąć z 'commit' ('unstage'), jak również sprawdzić, czy dodać zmiany dla poszczególnych plików. Alternaywnie możesz skorzystać z *git commit \--interactive*, polecenie to wykona automatycznie 'commit' gdy skończysz. + +=== Index: rusztowanie Gita === + +Do tej pory staraliśmy się omijać sławny 'index', jednak przyszedł czas się nim zająć, aby móc wyjaśnić wszystko to co poznaliśmy do tej pory. Index jest tymczasowym rusztowaniem Git rzadko wymienia dane bezpośrednio między twoim projektem a swoją historią wersji. Raczej zapisuje on dane najpierw w indexie, dopiero po tym kopiuje dane z indexu na ich właściwe miejsce przeznaczenia. + +Na przykład polecenie *commit -a* jest właściwie procesem dwustopniowym. Pierwszy krok to stworzenie obrazu bieżącego statusu każdego monitorowanego pliku do indeksu. Drugim krokiem jest trwałe zapamiętanie obrazu do indeksu. Wykonanie 'commit' bez opcji *-a* wykona jedynie drugi wspomniany krok i ma jedynie sens, jeśli poprzednio wykonano komendę, która dokonała odpowiednich zmian w indexie, na przykład *git add*. + +Normalnie możemy ignorować indeks i udawać, że czytamy i zapisujemy bezpośrednio z historii. W tym wypadku chcemy posiadać jednak większą kontrolę, więc manipulujemy indeks. Tworzymy obraz niektórych, jednak nie wszystkich zmian w indeksie i zapamiętujemy trwale starannie dobrany obraz. + +=== Nie trać głowy === + +Identyfikator 'HEAD' zachowuje się jak kursor, który zwykle wskazuje na najmłodszy 'commit' i z każdym nowym 'commit' zostaje przesunięty do przodu. Niektóre komendy Gita pozwolą ci nim manipulować. Na przyklad: + + $ git reset HEAD~3 + +przesunie identyfikator 'HEAD' o 3 'commits' spowrotem. Spowoduje to, że wszystkie następne komendy Gita będą reagować, jakby tych trzech ostatnich 'commits' wogóle nie było, podczas gdy twoje dane zostaną w przyszłości. Na stronach pomocy Gita znajdziesz więcej takich zastosowań. + +Ale jak teraz wrócić znów do przyszłości? Poprzednie 'commits' nic nie wiedzą o jej istnieniu. + +Jeśli posiadasz klucz SHA1 orginalnego 'HEAD', wtedy możesz wrócić komendą: + + $ git reset 1b6d + +Wyobraź jednak sobie, że nigdy go nie notowałeś? Nie ma sprawy: Przy wykonywaniu takich poleceń Git archiwizuje orginalny HEAD jako indentyfikator o nazwie ORIG_HEAD a ty możesz bezproblemowo wrócić używając: + + $ git reset ORIG_HEAD + +=== Łowcy głów === + +Może się zdarzyć, że ORIG_HEAD nie wystarczy. Może właśnie spostrzegłeś, iż dokonałeś kapitalnego błędu i musisz wrócić się do bardzo starego 'commit' w zapomnianym 'branch'. + +Standardowo Git zapamiętuje 'commit' przez przynajmniej 2 tygodnie, nawet jeśli poleciłeś zniszczyć 'branch' w którym istniał. Problemem staje się tutaj odnalezienie odpowieniego klucza SHA1. Możesz po kolei testować wszystkie klucze SHA1 w `.git/objects` i w ten sposób próbować odnaleźć szukany 'commit'. Istnieje jednak na to dużo prostszy sposób. + +Git zapamiętuje każdy obliczony klucz SHA1 dla odpowiednich 'commit' w `.git/logs`. Podkatalog `refs` zawieza przebieg wszystkich aktywności we wszystkich 'branches', podczas gdy plik `HEAD` wszystkie klucze SHA1 które kiedykolwiek posiadał. Ostatnie możemy zastosować do odnalezienia kluczy SHA1 tych 'commits' które znajdowały się w nieuważnie usuniętym 'branch'. + +Polecenie 'reflog' daje nam do dyspozycji przyjazny interfejs do tych właśnie logów. Wypróbuj polecenie: + + $ git reflog + +Zamiast kopiować i wklejać klucze z 'reflog', możesz: + + $ git checkout "@{10 minutes ago}" + +Albo przywołaj 5 z ostatnio oddwiedzanych 'commits' za pomocą: + + $ git checkout "@{5}" + +Jeśli chciałbyś pogłębić wiedze na ten temat przeczytaj sekcję ``Specifying Revisions`` w *git help rev-parse*. + +Byś może zechcesz zmienić okres karencji dla przeznaczonych na stracenie 'commits'. Na przyklad: + + $ git config gc.pruneexpire "30 days" + +znaczy, że skasowany 'commit' zostanie nieuchronnie utracony dopiero po 30 dniach od wykonania polecenia *git gc*. + +Jeśli chcałbyś zapobiec automatyycznemu wykonywaniu *git gc*: + + $ git config gc.auto 0 + +wtedy 'commits' będą tylko wtedy usuwane, gdy ręcznie wykonasz polecenie *git gc*. + +=== Budować na bazie Gita === + +W prawdziwym unixowym świecie sama konstrukcja Gita pozwala na wykorzystanie go, jako komponent niskiego poziomu, przez inne aplikacje, jak na przykład interfejsy graficzne i aplikacje internetowe, alternatywne narzędzia konsoli, narzędzia patchujące, narzędzia pomocne w importowaniu i konwertowaniu i tak dalej. Nawek same polecenia git są czasami malutkimi skryptami, jak krasnoludki na ramieniu olbrzyma. Przykładając trochę ręki możesz adoptować Git do twoich własnych potrzeb. + +Prostą sztuczką może być korzystanie z zintegrowanej w git funkcji aliasu, by skrócić najczęściej stosowane polecenia: + + $ git config --global alias.co checkout + $ git config --global --get-regexp alias # wyświetli aktualne aliasy + alias.co checkout + $ git co foo # to samo co 'git checkout foo' + +Czymś troszeczkę innym będzie zapis nazwy aktualnego 'branch' w prompcie lub jako nazwy okna. Polecenie: + + $ git symbolic-ref HEAD + +pokaże nazwę aktualnego 'branch'. W praktyce chciałbyś raczej usunąć "refs/heads/" i ignorować błędy: + + $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- + +Podkatakog +contrib+ jest wielkim znaleziskiem narzędzi zbudowanych dla git. Z czasem niektóre z nich mogą uzyskać status oficjalnych poleceń. W dystrybucji Debian i Ubuntu znajdziemy ten katalog pod +/usr/share/doc/git-core/contrib+. + +Ulubionym przedstawicielem jest +workdir/git-new-workdir+. Poprzez sprytne przelinkowania skrypt ten tworzy nowy katalog roboczy, który dzieli swoją historię wersji z orginalnym repozytorium: + + $ git-new-workdir istniejacy/repo nowy/katalog + +Ten nowy katalog i znajdujące się w nim pliki można sobie wyobrazić jako klon, z tą różnicą, że ze względu na wspólną niepodzieloną historie obje wersje pozostaną zsynchronizowane. Synchronizacja za pomocą 'merge', 'push', czy 'pull' nie będzie konieczna. + +=== Śmiałe wyczyny === + +Obecnie Git dość dobrze chroni użytkownika przed przypadkowym zniszczeniem danych. Ale, jeśli wiemy co robić, możemy obejść środki ochrony najczęściej stosowanych poleceń. + +*Checkout*: nie wersjonowane zmiany doprowadzą do niepowodzenia polecenia 'checkout'. Aby mimo tego zniszczyć zmiany i przywołać istniejący 'commit', możemy skorzystać z opcji 'force': + + $ git checkout -f HEAD^ + +Jeśli poleceniu 'checkout' podamy inną ścieżkę, środki ochrony nie znajdą zastosowania. Podana ścieżka zostanie bez pytania zastąpiona. Bądź ostrożny stosując 'checkout' w ten sposób. + +*reset*: reset odmówi pracy, jeśli znajdzie niewersjonowane zmiany. By zmusić go do tego, możesz użyć: + + $ git reset --hard 1b6d + +*Branch*: Skasowanie 'branches' też się nie powiedzie, jeśli mogłyby przez to zostać utracone zmiany. By wymusić skasowanie, podaj: + + $ git branch -D martwy_branch # zamiast -d + +Również nie uda się próba przesunięcia 'branch' poleceniem 'move', jeśliby miałoby to oznaczać utratę danych. By wymusić przesunięcie, podaj: + + $ git branch -M źródło cel # zamiast -m + +Inaczej niż w przypadku 'checkout' i 'reset', te oba polecenia przesuną zniszczenie danych. Zmiany te zostaną zapisane w podkatalogu .git i mogą znów zostać przywrócone, jeśli znajdziemy odpowiedni kluch SHA1 w `.git/logs` (zobacz "Łowcy głów" powyżej). Standardowo dane te pozostają jeszcze przez 2 tygodnie. + +*clean*: Różnorakie polecenia Git nie chcą się zadziałać, ponieważ podejżewają konflikty z niewersjonowanymi danymi. Jeśli jesteś pewny, że wszystkie niezwersjonowane pliki i katalogi są zbędne, skasujesz je bezlitośnie poleceniem: + + $ git clean -f -d + +Następnym razem te uciążliwe polecenia zaczną znów być posłuszne. + +=== Zapobiegaj złym 'commits' === + +Głupie błędy zaśmiecają moje repozytoria. Najbardziej fatalny jest brak plików z powodu zapomnianych *git add*. Mniejszymi usterkami mogą być spacje na końcu linii i nierozwiązane konflikty poleceń 'merge': mimo iż nie są groźne, życzyłbym sobie, by nigdy nie wystąpiły publicznie. + +Gdybym tylko zabezpieczył się wcześniej, stosując prosty _hook_, który alarmowałby mnie przy takich problemach. + + $ cd .git/hooks + $ cp pre-commit.sample pre-commit # Starsze wersje Gita wymagają jeszcze: chmod +x pre-commit + +I już 'commit' przerywa, jeśli odkryje niepotrzebne spacje na końcu linii albo nierozwiązane konflikty 'merge'. + +Na początku *pre-commit* tego 'hook' umieściłbym dla ochrony przed rozdrobnieniem: + + if git ls-files -o | grep '\.txt$'; then + echo FAIL! Untracked .txt files. + exit 1 + fi + +Wiele operacji Gita pozwala na używanie 'hooks'; zobacz też: *git help hooks*. We wcześniejszcm rozdziale "Git poprzez http" przytoczyliśmy przykład 'hook' dla *post-update*, który wykonywany jest zawsze, jeśli znacznik 'HEAD' zostaje przesunięty. Ten przykładowy skrypt 'post-update' aktualizuje dane, które potrzebne są do komunikacji poprzez 'Git-agnostic transports', jak na przykład HTTP. diff --git a/szl/history.txt b/szl/history.txt new file mode 100644 index 0000000..2f00b35 --- /dev/null +++ b/szl/history.txt @@ -0,0 +1,198 @@ +== Lekcja historii == + +Jedną z charakterystycznych cech rozproszonej natury Git jest to, że jego kronika historii może być łatwo edytowana. Ale jeśli masz zamiar manipulować przeszłością, bądź ostrożny: zmieniaj tylko tą część historii, którą wyłącznie jedynie ty sam posiadasz. Tak samo jak Narody ciągle dyskutują, który jakie popełnił okrucieństwa, popadniesz w kłopoty przy synchronizacji, jeśli ktoś inny posiada klon z różniącą się historią i jeśli te odgałęzienia mają się wymieniać. + +Niektórzy programiści zarzekają się w kwestii nienaruszalności historii - ze wszystkimi jej błędami i niedociągnięciami. Inni uważają, ze odgałęzienia powinny dobrze się prezentować nim zostaną przedstawione publicznie. Git jest wyrozumiały dla obydwóch stron. Tak samo jak 'clone', 'branch' czy 'merge', możliwość zmian kroniki historii to tylko kolejna mocna strona Gita. Stosowanie lub nie, tej możliwości zależy wyłącznie od ciebie. + +=== Muszę się skorygować === + +Właśnie wykonałeś 'commit', ale chętnie chciałbyś podać inny opis? Wpisujesz: + + $ git commit --amend + +by zmienić ostatni opis. Zauważasz jednak, że zapomniałeś dodać jakiegoś pliku? Wykonaj *git add*, by go dodać a następnie poprzednią instrukcje. + +Chcesz wprowadzić jeszcze inne zmiany do ostatniego 'commit'? Wykonaj je i wpisz: + +$ git commit --amend -a + +=== ... i jeszcze coś === + +Załóżmy, że poprzedni problem będzie 10 razy gorszy. Po dłuższej sesji zrobiłeś całą masę 'commits'. Nie jesteś jednak szczęśliwy z takiego zorganizowania a niektóre z 'commits' mogłyby być inaczej sformułowane. Wpisujesz: + + $ git rebase -i HEAD~10 + +i ostatnie 10 'commits' pojawią się w preferowanym przez ciebie edytorze. Przykładowy wyciąg: + + pick 5c6eb73 Added repo.or.cz link + pick a311a64 Reordered analogies in "Work How You Want" + pick 100834f Added push target to Makefile + +Starsze 'commits' poprzedzają młodsze, inaczej niż w poleceniu `log` +Tutaj 5c6eb73 jest najstarszym 'commit', a 100834f najnowszym. By to zmienić: + +- Usuń 'commits' poprzez skasowanie linii. Podobnie jak polecenie 'revert', będzie to jednak wyglądało jakby wybrane 'commit' nigdy nie istniały. +- Przeorganizuj 'commits' przesuwając linie. +- Zamień `pick` na: + * `edit` by zaznaczyć 'commit' do 'amend'. + * `reword`, by zmienić opisy logu. + * `squash` by połączyć 'commit' z poprzednim ('merge'). + * `fixup` by połączyć 'commit' z poprzednim ('merge') i usunąć zapisy z logu. + +Na przykład chcemy zastąpić drugi `pick` na `squash`: + +Zapamiętaj i zakończ. Jeśli zaznaczyłeś jakiś 'commit' poprzez 'edit', wpisz: + + $ git commit --amend + + pick 5c6eb73 Added repo.or.cz link + squash a311a64 Reordered analogies in "Work How You Want" + pick 100834f Added push target to Makefile + +After we save and quit, Git merges a311a64 into 5c6eb73. Thus *squash* merges +into the next commit up: think ``squash up''. + +Git then combines their log messages and presents them for editing. The +command *fixup* skips this step; the squashed log message is simply discarded. + +If you marked a commit with *edit*, Git returns you to the past, to the oldest +such commit. You can amend the old commit as described in the previous section, +and even create new commits that belong here. Once you're pleased with the +``retcon'', go forward in time by running: + + $ git rebase --continue + +Git replays commits until the next *edit*, or to the present if none remain. + +You can also abandon the rebase with: + + $ git rebase --abort + +A więc, stosuj polecenie 'commit' wcześnie i często: możesz później posprzątać za pomocą 'rebase'. + +=== Lokalne zmiany na koniec === + +Pracujesz nad aktywnym projektem. Z biegiem czasu nagromadziło się wiele 'commits' i wtedy chcesz zsynchronizować za pomocą 'merge' z oficjalną gałęzią. Ten cykl powtarza się kilka razy zanim jesteś gotowy na 'push' do centralnego drzewa. + +Teraz jednak historia w twoim lokalnym klonie jest chaotycznym pomieszaniem twoich zmian i zmian z oficjalnego drzewa. Chciałbyś raczej widzieć twoje zmiany uporządkowane chronologicznie w jednej sekcji i za oficjalnymi zmianami. + +To zadanie dla *git rebase*, jak opisano powyżej. W wielu przypadkach możesz skorzystać z przełącznika *--onto* by zapobiec interakcji. + +Przeczytaj też *git help rebase* dla zapoznania sie z obszernymi przykładami tej zadziwiającej funkcji. Możesz również podzielić 'commits'. Możesz nawet przeorganizować 'branches' w repozytorium. + +Bądź ostrożny korzystając z 'rebase', to bardzo mocne polecenie. Zanim dokonasz skomplikowanych 'rebase', zrób backup za pomocą *git clone* + +=== Przepisanie historii === + +Czasami potrzebny ci rodzaj systemu kontroli porównywalnego do wyretuszowania osób z oficjalnego zdjęcia, by w stalinowski sposób wymazać je z historii. Wyobraź sobie, że chcesz opublikować projekt, jednak zawiera on pewny plik, który z jakiegoś ważnego powodu musi pozostać utajniony. Być może zapisałem numer karty kredytowej w danej tekstowej i nieumyślnie dodałem do projektu? Skasowanie tej danej nie ma sensu, ponieważ poprzez starsze 'commits' można nadal ją przywołać. Musimy ten plik usunąć ze wszystkich 'commits': + + $ git filter-branch --tree-filter 'rm bardzo/tajny/plik' HEAD + +Sprawdź *git help filter-branch*, gdzie przykład ten został wytłumaczony i przytoczona została jeszcze szybsza metoda. Ogólnie poprzez *filter-branch* da się dokonać zmian w dużych zakresach historii poprzez tylko jedno polecenie. + +Po tej operacji katalog +.git/refs/original+ opisuje stan przed jej wykonaniem. Sprawdź czy 'filter-branch' zrobił to, co od niego oczekiwałeś, następnie skasuj ten katalog zanim wykonasz następne polecenia 'filter-branch'. + +Wreszcie zamień wszystkie klony twojego projektu na zaktualizowaną wersję, jeśli masz zamiar prowadzić z nimi wymianę. + +=== Tworzenie historii === + +[[makinghistory]] +Masz zamiar przenieść projekt do Gita? Jeśli twój projekt był dotychczas zarządzany jednym z bardziej znanych systemów, to istnieje duże prawdopodobieństwo, że ktoś napisał już odpowiedni skrypt, który umożliwi ci eksportowanie do Gita całej historii. + +W innym razie przyjrzyj się funkcji *git fast-import*, która wczytuje tekst w specjalnym formacie by następnie odtworzyć całą historię od początku. Często taki skrypt pisany jest pośpiesznie i służy do jednorazowego wykorzystania, aby tylko w jednym przebiegu udała się migracja projektu. + +Utwórz na przykład z następującej listy tymczasowy plik, na przykład jako: `/tmp/history`: +---------------------------------- +commit refs/heads/master +committer Alice Thu, 01 Jan 1970 00:00:00 +0000 +data < + +int main() { + printf("Hello, world!\n"); + return 0; +} +EOT + + +commit refs/heads/master +committer Bob Tue, 14 Mar 2000 01:59:26 -0800 +data < + +int main() { + write(1, "Hello, world!\n", 14); + return 0; +} +EOT + +---------------------------------- + +Następnie utwórz repozytorium git z tymczasowego pliku poprzez wpisanie: + + $ mkdir project; cd project; git init + $ git fast-import --date-format=rfc2822 < /tmp/history + +Aktualną wersję projektu możesz przywołać ('checkout') poprzez: + + $ git checkout master + +Polecenie *git fast-export* konwertuje każde repozytorium do formatu *git fast-import*, możesz przestudiować komunikaty tego polecenia, jeśli masz zamiar napisać programy eksportujące a oprócz tego, by przekazywać repozytoria jako czytelne dla ludzi zwykłe pliki tekstowe. To polecenie potrafi przekazywać repozytoria za pomocą zwykłego pliku tekstowego. + +=== Gdzie wszystko się zepsuło? === + +Właśnie znalazłeś w swoim programie funkcję, która już nie chce działać, a jesteś pewna, że czyniła to jeszcze kilka miesięcy temu. Ach! Skąd wziął się ten błąd? A gdybym tylko lepiej przetestowała ją wcześniej, zanim weszła do wersji produkcyjnej. + +Na to jest już za późno. Jakby nie było, pod warunkiem, że często używałeś 'commit', Git może ci zdradzić gdzie szukać problemu. + + $ git bisect start + $ git bisect bad HEAD + $ git bisect good 1b6d + +Git przywoła stan, który leży dokładnie pośrodku. Przetestuj funkcję, a jeśli ciągle jeszcze nie działa: + + $ git bisect bad + +Jeśli nie, zamień "bad" na "good". Git przeniesie cię znowu do stanu dokładnie pomiędzy znanymi wersjami "good" i "bad", redukując w ten sposób możliwości. Po kilku iteracjach doprowadzą cię te poszukiwania do 'commit', który jest odpowiedzialny za kłopoty. Po skończeniu dochodzenia przejdź do oryginalnego stanu: + + $ git bisect reset + +Zamiast sprawdzania zmian ręcznie, możesz zautomatyzować poszukiwania za pomocą skryptu: + + $ git bisect run mój_skrypt + +Git korzysta tutaj z wartości zwróconej przez skrypt, by ocenić czy zmiana jest dobra ('good'), czy zła ('bad'): Skrypt powinien zwracać 0 dla 'good', 128, jeśli zmiana powinna być pominięta, i coś pomiędzy 1 - 127 dla 'bad'. Jeśli wartość zwrócona jest ujemna, program 'bisect' przerywa pracę. + +Możesz robić jeszcze dużo innych rzeczy: w pomocy znajdziesz opis w jaki sposób wizualizować działania 'bisect', sprawdzić czy powtórzyć log bisect, wyeliminować nieistotne zmiany dla zwiększenia prędkości poszukiwań. + +=== Kto ponosi odpowiedzialność? === + +Jak i wiele innych systemów kontroli wersji również i Git posiada polecenie 'blame': + + $ git blame bug.c + +które komentuje każdą linię podanego pliku, by pokazać kto ją ostatnio zmieniał i kiedy. W przeciwieństwie do wielu innych systemów, funkcja ta działa offline, czytając tylko z lokalnego dysku. + +=== Osobiste doświadczenia === + +W scentralizowanym systemie kontroli wersji praca nad kroniką historii jest skomplikowanym zadaniem i zarezerwowanym głównie dla administratorów. Polecenia 'clone', 'branch' czy 'merge' nie są możliwe bez podłączenia do sieci. Również takie podstawowe funkcje, jak przeszukanie historii czy 'commit' jakiejś zmiany. W niektórych systemach użytkownik potrzebuje działającej sieci nawet by zobaczyć dokonane przez siebie zmiany, albo by w ogóle otworzyć plik do edycji. + +Scentralizowane systemy wykluczają pracę offline i wymagają drogiej infrastruktury sieciowej, w szczególności gdy wzrasta liczba programistów. Najgorsze jednak, iż z czasem wszystkie operacje stają się wolniejsze, z reguły do osiągnięcia punktu, gdzie użytkownicy unikają zaawansowanych poleceń, aż staną się one absolutnie konieczne. W ekstremalnych przypadkach dotyczy to również poleceń podstawowych. Jeśli użytkownicy są zmuszeni do wykonywania powolnych poleceń, produktywność spada, ponieważ ciągle przerywany zostaje tok pracy. + +Dowiedziałem się o tym fenomenie z pierwszej ręki. Git był pierwszym systemem kontroli wersji którego używałem. Szybko dorosłem do tej aplikacji i przyjąłem wiele funkcji za oczywiste. Wychodziłem też z założenia, że inne systemy są podobne: wybór systemu kontroli wersji nie powinien zbyt bardzo odbiegać od wyboru edytora tekstu, czy przeglądarki internetowej. + +Byłem zszokowany, gdy musiałem później korzystać ze scentralizowanego systemu. Niesolidne połączenie internetowe ma niezbyt duży wpływ na Gita, praca staje się jednak prawie nie możliwa, gdy wymagana jest niezawodność porównywalny z lokalnym dyskiem. Poza tym sam łapałem się na tym, że unikałem pewnych poleceń i związanym z nimi czasem oczekiwania, w sumie wszystko to wpływało mocno na wypracowany przeze mnie system pracy. + +Gdy musiałem wykonywać powolne polecenia, z powodu ciągłego przerywanie toku myślenia, powodowałem nieporównywalne szkody dla całego przebiegu pracy. Podczas oczekiwania na zakończenie komunikacji pomiędzy serwerami dla przeczekania zaczynałem robić coś innego, na przykład czytałem maile albo pisałem dokumentację. Gdy wracałem do poprzedniego zajęcia, po zakończeniu komunikacji, dawno straciłem wątek i traciłem czas, by znów przypomnieć sobie co właściwie miałem zamiar zrobić. Ludzie nie potrafią dobrze dostosować się do częstej zmiany kontekstu. + +Był też taki ciekawy efekt http://pl.wikipedia.org/wiki/Tragedia_wspólnego_pastwiska[tragedii wspólnego pastwiska]: przypominający przeciążenia w sieci - pojedyncze indywidua pochłaniają więcej pojemności sieci niż to konieczne, by uchronić się przed mogącymi ewentualnie wystąpić w przyszłości niedoborami. Suma tych starań pogarsza tylko przeciążenia, co motywuje jednostki do zużywania jeszcze większych zasobów, by ochronić się przed jeszcze dłuższymi czasami oczekiwania. diff --git a/szl/intro.txt b/szl/intro.txt new file mode 100644 index 0000000..78cd482 --- /dev/null +++ b/szl/intro.txt @@ -0,0 +1,57 @@ +== Wprowadzenie == + +By wprowadzić w zagadnienie kontroli wersji, posłużę się pewną analogią. Dla bardziej rozsądnego wyjaśnienia przeczytajcie http://pl.wikipedia.org/wiki/System_kontroli_wersji[Artykuł Wikipedii na temat systemu kontroli wersji]. + +=== Praca jest zabawą === + +Gram w gry komputerowe chyba już przez całe moje życie. W przeciwieństwie do tego, systemy kontroli wersji zacząłem używać dopiero jako dorosły. Przypuszczam, że nie jestem tu odosobniony, a porównanie to pomoże mi w prosty sposób wytłumaczyć zrozumiale jej koncept. + +Wyobraź sobie pracę nad twoim kodem albo dokumentami jak granie na komputerze. Jeśli dobrze ci poszło, chciałabyś zabezpieczyć swoje osiągnięcia. W tym celu klikasz na 'zapisz' w wybranym edytorze. + +Jednak, przepiszesz przez to poprzednio zapamiętaną wersję. To jak w grach starej szkoły, które posiadały pamięć na zapisanie tylko jednego stanu: oczywiście, mogłeś zapamiętać, ale już nigdy nie mogłeś powrócić do starszego stanu. To była hańba, bo być może poprzednio zabezpieczony stan był w jakimś bardzo interesującym miejscu gry, do którego chętnie chciałbyś jeszcze wrócić. Albo jeszcze gorzej, twój zabezpieczony stan gry utknął w miejscu niemożliwym do rozwiązania i musisz zaczynać wszystko od początku. + +=== Kontrola wersji === + +Podczas edytowania, by uchronić starą wersję, możesz poprzez wybranie 'zapisz jako ...' zapisać twój dokument pod inną nazwą lub zapamiętać w innym miejscu. Poza tym możesz go jeszcze spakować, by zaoszczędzić miejsce na dysku. Jest to prymitywna i pracochłonna forma kontroli wersji. Gry komputerowe robią tak już od długiego czasu, wiele z nich posiada automatycznie utworzone punkty opatrzone sygnaturą czasu. + +Skomplikujmy teraz trochę cały ten problem. Powiedzmy, że posiadasz całą masę plików, które w jakiś sposób są ze sobą powiązane, na przykład kod źródłowy jakiegoś projektu lub pliki strony internetowej. Jeśli chcesz otrzymać starszą wersję musisz archiwizować cały katalog. Archiwizowanie w ten sposób wielu wersji jest pracochłonne i szybko może stać się kosztowne, zabierając niepotrzebnie miejsce na dysku. + +Niektóre gry komputerowe składały się rzeczywiście z jednego katalogu pełnego plików. Gry ukrywały szczegóły przed graczem i prezentowały wygodny interfejs, do zarządzania różnymi wersjami katalogu. + +Systemy kontroli wersji nie różnią się tutaj zbytnio. Wszystkie posiadają wygodne interfejsy, aby zarządzać katalogami pełnymi plików. Możesz archiwizować stan katalogu tak często jak często chcesz i później możesz do każdego z tych punktów powrócić. W przeciwieństwie jednak do gier, są one z reguły wszystkie zoptymalizowane pod kątem oszczędności pamięci. W większości przypadków tylko niewiele danych ulega zmianie pomiędzy dwoma wersjami, a same zmiany nie są zbyt obszerne. Oszczędność miejsca na dysku polega głównie na zapamiętywaniu jedynie różnic, a nie kopii całego katalogu. + +=== Rozproszona kontrola === + +Wyobraź sobie teraz bardzo skomplikowaną grę komputerową. Tak trudną, że wielu doświadczonych graczy na całym świecie postanawia o wspólnych siłach przejść grę, wymieniając się w tym celu swoimi zapamiętanymi wynikami. 'Speedruns' mogą posłużyć jako przykład z prawdziwego życia: gracze, którzy wyspecjalizowali się w różnych poziomach gry współpracują ze sobą dla uzyskania fascynujących wyników. + +W jaki sposób skonstruowałbyś taki system, który w prosty sposób jest w stanie otrzymywać archiwa od innych? Natomiast własne innym udostępni? + +Kiedyś każdy projekt korzystał z własnego scentralizowanego systemu kontroli wersji. Jeden serwer zapamiętywał wszystkie gry, nikt inny. Każdy gracz posiadał jedynie kilka zapamiętanych na swoim komputerze gier. Jeśli jakiś gracz chciał popchać grę trochę do przodu, musiał najpierw zładować ze serwera aktualny stan, trochę pograć, zapisać własny stan, a następnie załadować na serwer, by mógł go wykorzystać ktoś inny. + +A gdy jakiś gracz z jakiegoś powodu chce otrzymać jakiś starszy stan? Może aktualnie zapamiętany stan gry nie jest do przejścia, bo ktoś na trzecim poziomie zapomniał zabrać jakiś obiekt, i teraz próbują znaleźć stan startując od którego gra staje się znowu możliwa do przejścia. Albo chcecie porównać dwa stany, by sprawdzić ile jakiś gracz przyczynił się. + +Jest wiele powodów, dla których można chcieć zobaczyć straszy stan, wynik jednak jest zawsze taki sam. Za każdym razem trzeba ściągnąć wszystkie dane z serwera. Czym więcej gier zostało zapamiętanych, tym więcej wymaga to komunikacji. + +Nowa generacja systemów kontroli wersji, do których należy również git, nazywana jest systemami rozproszonymi i mogą być rozumiane jako uogólnienie systemów scentralizowanych. Jeśli gracze ładują teraz z serwera, otrzymują każdy zapisany stan, nie tylko ostatnio zapisany Wygląda to jak klonowanie serwera. + +Pierwszy klon może być drogi, przede wszystkim, jeśli posiada długą historię, ale na dłuższy okres opłaci się. Jedną z bezpośrednich zalet jest to, że kiedykolwiek potrzebny będzie jakiś starszy stan, komunikacja z głównym serwerem będzie zbędna. + +=== Głupi przesąd === + +Szeroko rozpowszechnianym nieporozumieniem jest opinia, że rozproszony system nie nadaje się dla projektów wymagających oficjalnego centralnego repozytorium. Nic nie jest bardziej oddalone od rzeczywistości. Fotografując kogoś nie kradniemy jego duszy. Tym samym klonowanie centralnego repozytorium nie umniejsza jego znaczenia. + +Jednym z pierwszych pozytywnych zbliżeń, jest to, iż wszystko co potrafi scentralizowany system kontroli wersji, dobrze dopracowany system rozproszony potrafi lepiej. Zasoby sieciowe są po prostu droższe niż zasoby lokalne. Również, gdy w późniejszym czasie dostrzeżemy wady systemów rozproszonych, można to przyjąć jako ogólną zasadę mniej podatną na złe porównania. + +Mały projekt wykorzysta prawdopodobnie tylko ułamek możliwości systemu. Ale, by z tego powodu korzystać z prostego systemu, nie posiadającego możliwości rozszerzenia, to tak jak stosowanie rzymskich cyfr do przeprowadzania obliczeń na małych liczbach. + +Poza tym może się zdarzyć, że twój projekt daleko przerośnie początkowe oczekiwania. Używając git od samego początku, to jak noszenie ze sobą scyzoryka szwajcarskiego, nawet gdy najczęściej posłuży do otwierania butelek. Być może pewnego dnia będziesz pilnie potrzebowała użyć śrubokrętu, ucieszysz się, że masz przy sobie coś więcej niż tylko zwykły otwieracz do butelek. + +=== Konflikty z 'merge' === + +Do przedstawienia tego punktu wykorzystanie analogii do gier komputerowych nie jest odpowiednia. Zamiast tego wyobraźmy sobie znowu, że edytujemy jakiś dokument. + +Wyobraź sobie, Alicja dodaje linijkę na początku dokumentu, natomiast Bob na jego końcu. Obydwoje ładują swoje zmiany na serwer. Większość systemów wybierze automatycznie rozsądną drogę: zaakceptuje obie zmiany i połączy je ze sobą, tym samym obje poprawki wpłyną do dokumentu. + +Wyobraź sobie jednak, że Alicja i Bob dokonują zmian w tej samej linii. W tym wypadku dalsze praca nie będzie możliwa bez ludzkiego udziału. Druga z osób, próbująca zładować dokument na serwer, zostanie poinformowana o wystąpieniu konfliktu 'merge' i musi zdecydować, która ze zmian zostanie przyjęta lub ponownie zrewidować całą linijkę. + +Mimo to mogą wystąpić dużo bardziej skomplikowane sytuacje. Systemy kontroli wersji potrafią poradzić sobie z prostymi przypadkami a te trudniejsze pozostawiają ludziom. Zazwyczaj ich zachowanie daje się ustawić. diff --git a/szl/multiplayer.txt b/szl/multiplayer.txt new file mode 100644 index 0000000..5b5993b --- /dev/null +++ b/szl/multiplayer.txt @@ -0,0 +1,169 @@ +== Multiplayer Git == + +Na początku zastosowałem git przy prywatnym projekcie, gdzie byłem jedynym developerem. Z poleceń w związku z rozproszoną naturą git, potrzebowałem jedynie poleceń *pull* i *clone*, dzięki czemu mogłem trzymać ten sam projekt w kilku miejscach. + +Później chciałem opublikować mój kod za pomocą git i dołączyć zmiany kolegów Musiałem nauczyć się zarządzać projektami, nad którymi zaangażowani byli programiści z całego świata. Na szczęście jest to silną stroną git i chyba jego racją bytu. + +=== Kim jestem? === + +Każdy 'commit' otrzymuje nazwę i adres e-mail autora, które zostaną pokazane w *git log*. Standardowo git korzysta z ustawień systemowych do wypełnienia tych pól. Aby wprowadzić te dane bezpośrednio, podaj: + + $ git config --global user.name "Jan Kowalski" + $ git config --global user.email jan.kowalski@example.com + +Jeśli opóścisz przełącznik '--global' zmiany zostaną zastosowane wyłącznie do aktualnego repozytorium. + +=== Git przez SSH, HTTP === + +Załóżmy, posiadasz dostęp 'SSH' do serwera stron internetowych, gdzie jednak Git nie został zainstalowany. Nawet, jeśli jest to mniej efektywne jak własny protokół 'GIT', Git potrafi komunikować się przez 'HTTP'. + +Zładuj Git, skompiluj i zainstaluj pod własnym kontem oraz utwórz repozytorium w twoim katalogu strony internetowej. + + $ GIT_DIR=proj.git git init + $ cd proj.git + $ git --bare update-server-info + $ cp hooks/post-update.sample hooks/post-update + +Przy starszych wersjach Gita samo polecenie 'cp' nie wystarczy, wtedy musisz jeszcze: + + chmod a+x hooks/post-update + +Od teraz możesz publikować aktualizacje z każdego klonu poprzez SSH. + + $ git push web.server:/sciezka/do/proj.git master + +i każdy może teraz sklonować twój projekt przez: + + $ git clone http://web.server/proj.git + +=== Git ponad wszystko === + +Chciałbyś synchronizować repozytoria bez pomocy serwera czy nawet bez użycia sieci komputerowej? Musisz improwizować w nagłym wypadku? Widzieliśmy, że poleceniami <>. W ten sposób możemy transportować tego typu pliki za pomocą dowolnego medium, jednak bardziej wydajnym narzędziem jest *git bundle*. + +Nadawca tworzy 'bundle': + + $ git bundle create plik HEAD + +i transportuje 'bundle' +plik+ do innych zaangażowanych: przez e-mail, pendrive, *xxd* hexdump i skaner OCR, kod morsea, przez telefon, znaki dymne itd. Odbiorca wyciąga 'commits' z 'bundle' poprzez podanie: + + + $ git pull plik + +Odbiorca może to zrobić z pustym repozytorium. Mimo swojej wielkości +plik+ zawiera kompletny oryginał repozytorium. + +W dużych projektach unikniesz śmieci danych, jeśli tylko zrobisz 'bundle' zmian brakujących w innych repozytoriach. Na przykład załóżmy, że 'commit' ``1b6d...'' jest najaktualniejszym, które posiadają obie partie: + + + $ git bundle create plik HEAD ^1b6d + +Jeśli robi się to regularnie, łatwo można zapomnieć, który 'commit' został wysłany ostatnio. Strony pomocy zalecają stosowanie tagów, by rozwiązać ten problem. To znaczy, po wysłaniu 'bundle', podaj: + + $ git tag -f ostatni_bundle HEAD + +a nowy 'bundle' tworzymy następnie poprzez: + + $ git bundle create nowy_bundle HEAD ^ostatni_bundle + +=== Patches: globalny środek płatniczy === + +'Patches' to jawne zobrazowanie twoich zmian, które mogą być jednocześnie rozumiane przez komputer i człowieka. Dodaje im to uniwersalnej mocy przyciągania. Możesz wysłać patch prowadzącym projekt, niezależnie od tego, jakiego używają systemu kontroli wersji. Doputy twoi współpracownicy potrafią czytać swoje maile, mogą widzieć również twoje zmiany. Również i z twojej strony wszystko, czego ci potrzeba to funkcjonujące konto e-mailowe: nie istnieje konieczność zakładania repozytorium online. + +Przypomnij sobie pierwszy rozdział: + + $ git diff 1b6d > mój.patch + +produkuje 'patch', który można dołączyć do maila dla dalszej dyskusji. W repozytorium git natomiast podajesz: + + $ git apply < mój.patch + +By zastosować patch. + +W bardziej oficjalnym środowisku, jeśli nazwiska autorów i ich sygnatury powinny również być notowane, twórz 'patch' od pewnego punktu, po wpisaniu: + + $ git format-patch 1b6d + +Uzyskane w ten sposób dane mogą przekazane być do *git-send-mail* albo odręcznie wysłane. Możesz podać grupę 'commits' + + $ git format-patch 1b6d..HEAD^^ + +Po stronie odbiorcy zapamiętaj e-mail jako daną i podaj: + + $ git am < email.txt + +Patch zostanie wprowadzony i utworzy commit, włącznie z informacjami jak na przykład informacje o autorze. + +Jeśli stosujesz webmail musisz ewentualnie poszukać opcji pokazania treści w formie niesformatowanego textu , zanim zapamiętasz patch do pliku. + +Występują minimalne różnice między aplikacjami e-mailowymi bazującymi na mbox, ale jeśli korzystasz z takiej, należysz do grupy ludzi która za pewne umie się z nimi obchodzić bez czytania instrukcji! + +=== Przepraszamy, przeprowadziliśmy się === + +Po sklonowaniu repozytorium, polecenia *git push* albo *git pull* będą automatycznie wskazywały na oryginalne URL. Jak Git to robi? Tajemnica leży w konfiguracji, która utworzona zostaje podczas klonowania. Zaryzykujmy spojrzenie: + + $ git config --list + +Opcja +remote.origin.url+ kontroluje źródłowe URL; ``origin'' to alias, nadany źródłowemu repozytorium. Tak jak i przy konwencji z ``master'' 'Branch' , możemy ten alias zmienić albo skasować, zwykle jednak nie ma powodów by to robić. + +Jeśli oryginalne repozytorium zostanie przesunięte, możemy zaktualizować link poprzez: + + $ git config remote.origin.url git://nowy_link/proj.git + +Opcja +branch.master.merge+ definiuje standardowy remote-'Branch' dla *git pull*. Podczas początkowego klonowania, zostanie ustawiony na aktualny branch źródłowego repozytorium, że nawet i po tym jak 'HEAD' źródłowego repozytorium przejdzie do innego branch, późniejszy 'pull' pozostanie wierny oryginalnemu branch. + +Ta opcja jest ważna jedynie dla repozytorium, z którego dokonało się pierwsze klonowanie, co zapisane jest w opcji +branch.master.remote+. Przy 'pull' z innego repozytorium musimy podać z którego branch chcemy korzystać. + + $ git pull git://example.com/inny.git master + +To wyjaśnia dlaczego nasze poprzednie przykłady z 'push' i 'pull' nie posiadały argumentów. + +=== Oddalone 'Branches' === + +Jeśli klonujesz repozytorium, klonujesz również wszystkie jego 'branches' Może jeszcze tego nie zauważyłeś, ponieważ Git je ukrywa: musisz się o nie specjalnie pytać: To zapobiega temu, że branches z oddalonego repozytorium nie przeszkadzają twoim lokalnym branches i czyni to Git łatwiejszym dla początkujących. + +Oddalone 'branches' możesz pokazać poprzez: + + $ git branch -r + +Powinieneś zobaczyć coś jak: + +origin/HEAD origin/master origin/experimental + +Lista ta ukazuje branches i HEAD odległego repozytorium, które mogą być również stosowane w zwykłych poleceniach Git. Przyjmijmy, na przykład, że wykonałeś wiele commits i chciałbyś uzyskać porównanie do ostatnio ściągniętej wersji. Możesz przeszukać logi za odpowiednim kluczem SHA1, ale dużo prościej jest podać: + + $ git diff origin/HEAD + +Możesz też sprawdzić co działo się w 'Branch' ``experimental'' + + $ git log origin/experimental + +=== Więcej serwerów === + +Przyjmijmy, dwóch innych programistów pracuje nad twoim projektem i chcielibyśmy mieć ich na oku. Możemy obserwować więcej niż jedno reposytorium jednocześnie: + + $ git remote add inny git://example.com/jakies_repo.git + $ git pull inny jakis_branch + +Teraz przyłączyliśmy jeden branch z dwóch repozytoriów i uzyskaliśmy łatwy dostęp do wszystkich branch z wszystkich repozytoriów. + + $ git diff origin/experimental^ inny/jakiś_branch~5 + +Co jednak zrobić, gdy chcemy porównać zmiany w nich bez wpływu na naszą pracę? Innymi słowami, chcemy zbadać ich branches bez importowania ich zmian do naszego katalogu roboczego. Zamiast 'pull' skorzystaj z: + + $ git fetch # Fetch z origin, jako standard. + $ git fetch inne # Fetch od drugiego programisty. + +Polecenie to załaduje jedynie historię Mimo, że nasz katalog pozostał bez zmian, możemy teraz referować z każdego repozytorium poprzez polecenia Gita, ponieważ posiadamy lokalną kopię. + +Przypomnij sobie, że 'pull' za kulisami to to samo co 'fetch' z następującym za nim *merge*. W normalnym wypadku wykonalibyśmy *pull*, bo chcielibyśmy przywołać również ostatnie 'commmits'. Ta przywołana sytuacja jest wyjątkiem wartym wspomnienia. + +Sprawdź *git help remote* by zobaczyć, jak usuwa się repozytoria, ignoruje pewne branches i więcej- + +=== Moje ustawienia === + +W moich projektach preferuję, gdy pomagający mi programiści przygotują własne repozytoria, z których mogę wykonać 'pull'. Większość hosterów Gita pozwala na utworzenie jednym kliknięciem twojego własnego forka innego projektu. + +Gdy przywołałem moją gałęź korzystam z poleceń Gita dla nawigacji i kontroli zmian, które najłepiej gdy są dobrze zorganizowane i udokumentowane. Wykonuję 'merge' moich własnych zmian i przeprowadzam ewentualnie dalsze zmiany. Gdy już jestem zadowolony, 'push' do zentralnego repozytorium.- + +Mimo, iż dość rzadko otrzymuję posty, jestem zdania, że ta metoda się opłaca. Zobacz http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[Post na Blogu Linusa Torvalds (po angielsku)]. + +Pozostając w świecie Gita jest wygodniejsze niż otrzymywanie patchów, ponieważ zaoszczędza mi to konwertowanie ich do 'commits' Gita. Poza tym Git martwi się o szczegóły, jak nazwa autora i adres e-maila, tak samo jak i o datę i godzinę oraz motywuje autora do opisywania swoich zmian. diff --git a/pl/preface.txt b/szl/preface.txt similarity index 52% copy from pl/preface.txt copy to szl/preface.txt index c9c7325..53857be 100644 --- a/pl/preface.txt +++ b/szl/preface.txt @@ -1,16 +1,16 @@ -= Git Magic = -Ben Lynn -August 2007 += Git Magic = +Ben Lynn +Sierpień 2007 -== Preface == +== Przedmowa == -http://git-scm.com/[Git] is a version control Swiss army knife. A reliable versatile multipurpose revision control tool whose extraordinary flexibility makes it tricky to learn, let alone master. +http://git-scm.com/[Git] to rodzaj scyzoryka szwajcarskiego dla kontroli wersji. Niezawodne, wielostronne narzędzie do kontroli wersji, jego niezwykła elastyczność sprawia trudności w poznaniu, nie wspominając o samym użyciu. -As Arthur C. Clarke observed, any sufficiently advanced technology is indistinguishable from magic. This is a great way to approach Git: newbies can ignore its inner workings and view Git as a gizmo that can amaze friends and infuriate enemies with its wondrous abilities. +Jak stwierdził Arthur C. Clarke, każda wystarczająco postępowa technologia jest porównywalna z magią. Jest to wspaniałe podejście, by zacząć pracę z Git: Początkujący mogą zignorować swoje wewnętrzne mechanizmy i ujrzeć Git jako rzecz, która urzeka przyjaciół swoimi niezwykłymi możliwościami, a przeciwników doprowadza do białej gorączki. -Rather than go into details, we provide rough instructions for particular effects. After repeated use, gradually you will understand how each trick works, and how to tailor the recipes for your needs. +Zamiast wchodzić głęboko w szczegóły, oferujemy proste instrukcje do odpowiednich funkcji. Podczas regularnego korzystania sam dojdziesz do tego, jak te sztuczki funkcjonują i jak możesz dopasować podane instrukcje na swoje własne potrzeby. -.Translations +.Tłumaczenia - link:/\~blynn/gitmagic/intl/zh_cn/[Simplified Chinese]: by JunJie, Meng and JiangWei. Converted to link:/~blynn/gitmagic/intl/zh_tw/[Traditional Chinese] via +cconv -f UTF8-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/fr/[French]: by Alexandre Garel, Paul Gaborit, and Nicolas Deram. Also hosted at http://tutoriels.itaapy.com/[itaapy]. @@ -21,39 +21,33 @@ Rather than go into details, we provide rough instructions for particular effect - link:/~blynn/gitmagic/intl/uk/[Ukrainian]: by Volodymyr Bodenchuk. - link:/~blynn/gitmagic/intl/vi/[Vietnamese]: by Trần Ngọc Quân; also http://vnwildman.users.sourceforge.net/gitmagic/[hosted on his website]. -.Other Editions +.Inne wydania - link:book.html[Single webpage]: barebones HTML, with no CSS. - link:book.pdf[PDF file]: printer-friendly. - http://packages.debian.org/gitmagic[Debian package], http://packages.ubuntu.com/gitmagic[Ubuntu package]: get a fast and local copy of this site. Handy http://csdcf.stanford.edu/status/[when this server is offline]. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Physical book [Amazon.com]]: 64 pages, 15.24cm x 22.86cm, black and white. Handy when there is no electricity. -=== Thanks! === -I'm humbled that so many people have worked on translations of these pages. I -greatly appreciate having a wider audience because of the efforts of those -named above. +=== Dziękuję! === -Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, Tyler Breisacher, Sonia Hamilton, Julian Haagsma, Romain Lespinasse, Sergey Litvinov, Oliver Ferrigni, David Toca, Сергей Сергеев, Joël Thieffry, and Baiju Muthukadan contributed corrections and improvements. +Jestem mile zaskoczony, że tak dużo ludzi pracowało nad przetłumaczeniem tych stron. bardzo doceniam to, że dzięki staraniom tylu wyżej wymienionych osób mam możliwość osiągnąć większy zakres czytelników. -François Marier maintains the Debian package originally created by Daniel -Baumann. +Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, i Tyler Breisacher przyczynilli się do poprawek i korektur. -John Hinnegan bought the http://www.gitmagic.com/[gitmagic.com] domain. +François Marier jest mentorem pakietu Debiana, który uprzednio utworzony został przez Daniela Baumann. -My gratitude goes to many others for your support and praise. I'm tempted to -quote you here, but it might raise expectations to ridiculous heights. +Chciałbym podziękować również wszystkim innym za ich pomoc i dobre słowo. Chciałem was tu wszystkich wyszczególnić, mogłoby to jednak wzbudzić oczekiwania w szerokim zakresie. -If I've left you out by mistake, please tell me or just send me a patch! +Gdybym o tobie przypadkowo zapomniał, daj mi znać albo przyślij mi po prostu patch. -=== License === +=== Licencja === -This guide is released under http://www.gnu.org/licenses/gpl-3.0.html[the GNU General Public License version 3]. Naturally, the source is kept in a Git -repository, and can be obtained by typing: +Ten poradnik publikowany jest na bazie licencji http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License Version 3]. Oczywiście, tekst źródłowy znajduje się w repozytorium Git i może zostać powielone przez: - $ git clone git://repo.or.cz/gitmagic.git # Creates "gitmagic" directory. + $ git clone git://repo.or.cz/gitmagic.git # Utworzy katalog "gitmagic". -or from one of the mirrors: +albo z lustrzanego serwera: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git diff --git a/szl/secrets.txt b/szl/secrets.txt new file mode 100644 index 0000000..d6da30d --- /dev/null +++ b/szl/secrets.txt @@ -0,0 +1,146 @@ +== Uchylenie tajemnicy == + +Rzućmy spojrzenie pod maskę silnika i wytłumaczymy w jaki sposób Git realizuje swoje cuda. Nie będę wchodził w szczegóły. Dla pogłębienia tematu odsyłam na http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[anielskojęzychny podręcznik użytkownika]. + +=== Niewidzialność === + +Jak to możliwe, że Git jest taki niepostrzeżony? Zapominając na chwilę o sporadycznych 'commits' i 'merges', możesz pracować w sposób, jakby kontrola wersji w ogóle nie istniała. To znaczy - do czasu aż będzie ci potrzebna. A oto chodzi, byś był zadowolony z tego, że Git cały czas czuwa nad twoją pracą + +Inne systemy kontroli wersji ciągle zmuszają cię do ciągłego borykania się z zagadnieniem samej kontroli i związanej z tym biurokracji. Pliki mogą być zabezpieczone przed zapisem, aż do momentu gdy uda ci się poinformować centralny serwer o tym, że chciałabyś nad nimi popracować. Przy wzroście liczby użytkowników nawet najprostsze polecenia stają się wolne jak ślimak. Gdy tylko zniknie sieć lub centralny serwer praca staje. + +W przeciwieństwie do tego, Git posiada kronikę całej swojej historii w podkatalogu .git twojego katalogu roboczego. Jest to twoja własna kopie całej historii, z którą mogłabyś pracować offline, aż do momentu gdy zechcesz wymienić dane z innymi. Posiadasz absolutną kontrolę nad losem twoich danych, ponieważ Git potrafi dla ciebie w każdej chwili odtworzyć zapamiętany poprzednio stan z właśnie podkatalogu .git. + +=== Integralność === + +Z kryptografią przez większość ludzi łączona jest poufność informacji, jednak równie ważnym jej celem jest zabezpieczenie danych. Właściwe zastosowanie kryptograficznych funkcji hashujących (funkcji skrótu) może uchronić przed nieumyślnym lub celowym zniszczeniem danych. + +Klucz hashujący SHA1 mogłabyś wyobrazić sobie jako składający się ze 160 bitów numer identyfikacyjny jednoznacznie opisujący dowolny łańcuch znaków, i który spotkasz w sowim życiu jeden jedyny raz. Nawet i więcej niż to: wszystkie łańcuchy znaków, jakie ludzkość przez wiele generacji stworzyła. + +Sam klucz SHA1 też jest łańcuchem znaków w formie bajtów. Możemy generować klucze SHA1 z łańcuchów samych zawierających klucze SHA1. Ta prosta obserwacja okazała się niesamowicie pożyteczna: jeśli cię to zainteresowało poszukaj informacji na temat 'hash chains'. Zobaczymy później w jaki sposób wykorzystuje je Git dla zapewnienia produktywności i integralności danych. + +Krótko mówiąc, Git przechowuje twoje dane w podkatalogu `.git/objects`, gdzie zamiast nazw plików znajdziesz numery identyfikacyjne. Poprzez wykorzystanie tych numerów identyfikacyjnych jako nazwy plików razem z kilkoma innymi trikami związanymi z plikami blokującymi i znacznikami czasu, Git zamienia twój prosty system plików na produktywną i solidną bazę danych. + +=== Inteligencja === + +Skąd Git wie o tym, że zmieniłaś nazwę jakiegoś pliku, jeśli nigdy go o tym wyraźnie nie poinformowałaś? Oczywiście, być może użyłaś polecenia *git mv*, jest to jednak to samo jakbyś użyła *git rm*, a następnie *git add*. + +Git poszukuje heurystycznie zmian nazw w następujących po sobie wersjach kopii. Dodatkowo potrafi czasami nawet znaleźć całe bloki z kodem przenoszonym tam i z powrotem między plikami! Mimo iż wykonuje kawał dobrej roboty, a ta właściwość staje się coraz lepsza, nie potrafi niestety jeszcze poradzić sobie z wszystkimi możliwymi przypadkami. Jeśli to u ciebie nie działa, spróbuj poszukać opcji rozszerzonego rozpoznawania kopii, aktualizacja samego Gita, też może pomóc. + +=== Indeksowanie === + +Dla każdego kontrolowanego pliku, Git zapamiętuje informacje o jego wielkości, czasie utworzenia i czasie ostatniej edycji w pliku znanym nam jako index. By ustalić, czy nastąpiła jakaś zmiana, Git porównuje stan aktualny ze stanem zapamiętanym w indeksie. Jeśli dane te nie różnią się, Git może pominąć czytanie zawartości pliku. + +Ponieważ sprawdzenie statusu pliku trwa dużo krócej niż jego całkowite wczytanie, to jeśli dokonałaś zmian tylko na kilku plikach Git zaktualizuje swój stan w mgnieniu oka. + +Stwierdziliśmy już wcześniej, że indeks jest przechowalnią (ang. staging area). Jak to możliwe, że stos informacji o statusie danych może być przechowalnią? Ponieważ polecenie 'add' transportuje pliki do bazy danych Git i aktualizuje informacje o ich statusie, podczas gdy polecenie 'commit' (bez opcji) tworzy commit tylko wyłącznie na podstawie informacji o statusie plików, ponieważ pliki te już się w tej bazie znajdują. + +=== Korzenie Git === + +Ten http://lkml.org/lkml/2005/4/6/121['Linux Kernel Mailing List' post] opisuje cały łańcuch zdarzeń, które inicjowały powstanie Git. Cały post jest archeologicznie fascynującą stroną dla historyków zajmujących się Gitem. + +=== Obiektowa baza danych === + +Każda wersja twoich danych jest przechowywana w obiektowej bazie danych, która znajduje się w podkatalogu `.git/objects`. Inne miejsca w `.git/` posiadają mniej ważne dane, jak indeks, nazwy gałęzi ('branch'), tagi, logi,konfigurację, aktualną pozycję HEAD i tak dalej. Obiektowa baza danych jest prosta, mimo to jednak elegancka i jest źródłem siły Gita. + +Każdy plik w `.git/objects` jest obiektem. Istnieją trzy rodzaje obiektów, które nas interesują: 'blob', 'tree' i 'commit'. + +=== Bloby === + +Na początek magiczna sztuczka. Wymyśl jakąś nazwę pliku, jakąkolwiek. W pustym katalogu: + + + $ echo sweet > TWOJA_NAZWA + $ git init + $ git add . + $ find .git/objects -type f + $ find .git/objects -type f + +Zobaczysz coś takiego: +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. + +Skąd mogłem to wiedzieć, mimo iż nie znałem nazwy pliku? Ponieważ wartość klucza SHA1 dla: + + "blob" SP "6" NUL "sweet" LF + +wynosi właśnie: aa823728ea7d592acc69b36875a482cdf3fd5c8d. Przy czym SP to spacja, NUL - to bajt zerowy, a LF to znak nowej linii ('newline'). Możesz to skontrolować wpisując: + + $ printf "blob 6\000sweet\n" | sha1sum + +Git pracuje asocjacyjnie (skojarzeniowo): dane nie są zapamiętywane na podstawie ich nazwy, tylko wartości ich własnego klucza SHA1 w pliku, który określamy mianem obiektu 'blob'. Klucz SHA1 możemy sobie wyobrazić jako niepowtarzalny numer identyfikacyjny zawartości pliku, co oznacza, że pliki adresowane są na podstawie ich zawartości. Początkowe `blob 6`, to jedynie adnotacja, która określa jedynie rodzaj obiektu i jego wielkość w bajtach, pozwala to na uproszczenie zarządzania wewnętrznego. + +Przez to właśnie mogłem 'przepowiedzieć' wynik. Nazwa pliku nie ma znaczenia, jedynie jego zawartość służy do utworzenia obiektu 'blob'. + +Pytasz się, a co w przypadku identycznych plików? Spróbuj dodać kopie twojej danej pod jakąkolwiek nazwą. Zawartość +.git/objects+ nie zmieni się, niezależnie ile kopii dodałaś. Git zapamięta zawartość pliku wyłącznie jeden raz. + +Na marginesie, dane w +.git/objects+ są spakowane poprzez 'zlib', nie powinieneś otwierać ich bezpośrednio. Przefiltruj je najpierw przez http://www.zlib.net/zpipe.c[zpipe -d], albo wpisz: + + $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d + +polecenie to pokaże ci zawartość obiektu jako tekst. + +=== 'Trees' === + +Gdzie są więc nazwy plików? Przecież muszą być gdzieś zapisane. Podczas wykonywania 'commit' Git troszczy się o nazwy plików: + + $ git commit # dodaj jakiś opis. + $ find .git/objects -type f + $ find .git/objects -type f + +Powinieneś ujrzeć teraz 3 obiekty. Tym razem nie jestem w stanie powiedzieć, jak nazywają się te dwa nowe pliki, ponieważ częściowo są zależne od nazwy jaką nadałeś plikom. Pójdźmy dalej, zakładając, że jedną z tych danych nazwałeś ``rose''. Jeśli nie, możesz zmienić opis, by wyglądał jakby był twój: + + $ git filter-branch --tree-filter 'mv TWOJA_NAZWA rose' + $ find .git/objects -type f + +Powinnaś zobaczyć teraz plik +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, ponieważ jest to klucz SHA1 jego zawartości. + +"tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d + +Sprawdź, czy plik na prawdę odpowiada powyższej zawartości przez polecenie: + + $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch + +Za pomocą 'zpipe' łatwo sprawdzić klucz SHA1: + + + $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum + +Sprawdzanie za pomocą 'cat-file' jest troszeczkę kłopotliwe, bo jego 'output' zawiera więcej niż tylko nieskomprymowany obiekt pliku. + +Nasz plik to tak zwany obiekt 'tree': lista wyrażeń, na którą składają się rodzaj pliku, jego nazwa i jego klucz SHA1. W naszym przykładzie typ pliku to 100644, co oznacza, że `rose` jest plikiem zwykłym, natomiast klucz SHA1 odpowiada kluczowi SHA1 obiektu 'blob' zawierającego zawartość `rose`. Inne możliwe rodzaje plików to programy, linki symboliczne i katalogi. W ostatnim przypadku klucz SHA1 wskazuje na obiekt 'tree'. + +Jeśli użyjesz polecenia 'filter-branch', otrzymasz stare objekty, które nie są już używane. Mimo iż automatycznie zostaną usunięte po upłynięciu okresu łaski, chcemy się ich pozbyć od zaraz, aby lepiej prześledzić następne przykłady. + + $ rm -r .git/refs/original + $ git reflog expire --expire=now --all + $ git prune + +W prawdziwych projektach powinnaś unikać takich komend, ponieważ zniszczą zabezpieczone dane. Jeśli chcesz posiadać czyste repozytorium, to najlepiej załóż nowy klon. Bądź też ostrożna przy bezpośredniej manipulacji +.git+: gdy równocześnie wykonywane jest polecenie Git i zgaśnie światło? Generalnie do kasowania referencji powinnaś używać *git update-ref -d*, nawet gdy ręczne usunięcie +ref/original+ jest dość bezpieczne. + +=== 'Commits' === + +Wytłumaczyliśmy dwa z trzech obiektów. Ten trzeci to obiekt 'commit' Jego zawartość jest zależna od opisu 'commit' jak i czasu jego wykonania. By wszystko do naszego przykładu pasowało, musimy trochę pokombinować. + + $ git commit --amend -m Shakespeare # Zmień ten opis. + $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Zmanipuluj znacznik czasowy i nazwę autora. + $ find .git/objects -type f + $ find .git/objects -type f + +Powinieneś znaleźć +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+, co odpowiada kluczowi SHA1 jego zawartości: + +"commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice 1234567890 -0800" LF "committer Bob 1234567890 -0800" LF LF "Shakespeare" LF + +Jak i w poprzednich przykładach możesz użyć 'zpipe' albo 'cat-file' by to sprawdzić. + +To jest pierwszy 'commit', przez to nie posiada matczynych 'commits'. Następujące 'commits' będą zawsze zawierać przynajmniej jedną linikę identyfikującą rodzica. + +=== Nie do odróżnienia od magii === + +Tajemnice Gita wydają się być proste. Wygląda to jak połączenie kilku skryptów, troszeczkę kodu C i w przeciągu kilku godzin jesteśmy gotowi: zmiksowanie podstawowych operacji na systemie danych obliczenia SHA1, przyprawione danymi blokującymy i synchronizacją dla stabilności. W sumie można by tak opisać najwcześniejsze wersje Gita. Tym niemniej, abstrahując od udanych trików pakujących, by oszczędnie odnosić się z pamięcią i udanych trików indeksujących by zaoszczędzić czas, wiemy jak Git sprawnie przemienia system danych i bazę danych, co jest optymalne dla kontroli wersji. + +Przyjmijmy, gdy jakikolwiek plik w obiektowej bazie danych ulegnie zniszczeniu poprzez błąd nośnika, to jego SHA1 nie będzie zgadzać się z jego zawartością, co od razu wskaże nam problem. Poprzez tworzenie kluczy SHA1 z kluczy SHA1 innych objektów, osiągniemy integralność danych na wszystkich poziomach. 'Commits' są elementarne, do znaczy, 'commit' nie potrafi zapamiętać jedynie części zmian: klucz SHA1 'commit' możemy obliczyć i zapamiętać dopiero po tym gdy zapamiętane zostały wszystkie obiekty 'tree', 'blob' i rodziców 'commit'. Obiektowa baza dynch jest odporna na nieoczekiwane przerwy, jak na przykład przerwanie dostawy prądu. + +Możemy przetrwać nawet podstępnego przeciwnika. Wyobraź sobie, ktoś ma zamiar zmienić treść jakiegoś pliku, która leży w jakiejś starszej wersji projektu. By sprawić pozory, że baza danych wygląda nienaruszona musiałby zmienić klucze SHA1 korespondujących obiektów, ponieważ plik zawiera teraz zmieniony sznur znaków. To znaczy również, że musiałabyś zmienić każdy klucz obiektu 'tree', które ją referują oraz w wyniku tego wszystkie klucze 'commits' zawierające obiekty 'tree' dodatkowo do pochodnych tych 'commits'. Oznacza to również, że klucz oficjalnego HEAD różni się od klucza HEAD manipulowanego repozytorium. Wystarczy teraz prześledzić ścieżkę różniących się kluczy SHA1, odnaleźć okaleczony plik, jak i 'commit' w którym po raz pierwszy wystąpił. + +Krótko mówiąc, dopuki reprezentujące ostatni commit 20 bajtów są zabezpieczone, sfałszowanie repozytorium Gita nie jest możliwe. + +A co ze sławnymi możliwościami Gita? +'Branching'? 'Merging'? 'Tags'? To szczegół. Aktualny HEAD przetrzymywany jest w pliku +.git/HEAD+, która posiada klucz SHA1 ostatniego 'commit'. Klucz SHA1 zostaje aktualizowany podczas wykonania 'commit', tak samo jak i przy wielu innych poleceniach. 'branches' to prawie to samo, są plikami zapamiętanymi w +.git/refs/heads+. 'Tags' również, znajdziemy je w +.git/refs/tags+, są one jednak aktualizowane poprzez serię innych poleceń. diff --git a/szl/translate.txt b/szl/translate.txt new file mode 100644 index 0000000..4081d23 --- /dev/null +++ b/szl/translate.txt @@ -0,0 +1,21 @@ +== Załącznik B: Przetłumaczyć to HOWTO == + +Aby przetłumaczyć moje HOWTO polecam wykonanie następujących poniżej kroków, wtedy moje skrypty będą w prosty sposób mogły wygenerować wersje HTML i PDF. Poza tym wszystkie tłumaszenia mogą być prowadzone w jednym repozytorium. + +Sklonuj texty źródłowe, następnie utwórz katalog o nazwie skrótu IETF przetłumaszonego języka: sprawdź http://www.w3.org/International/articles/language-tags/Overview.en.php[Artykół W3C o internacjonalizacji]. Na przykład, angielski to "en", a japoński to "ja". Skopiuj wszystkie pliki +txt+ z katalogu "en" do nowoutworzonego katalogu. + +Aby przykładowo przetłumaczyć to HOWTO na http://de.wikipedia.org/wiki/Klingonische_Sprache[Klingonisch], musisz wykonać następujące polecenia: + + $ git clone git://repo.or.cz/gitmagic.git + $ cd gitmagic + $ mkdir tlh # "tlh" jest skrótem IETF języka Klingonisch. + $ cd tlh $ cp ../en/intro.txt . + $ edit intro.txt # Przetłumacz ten plik. + +i zrób to z każdą następną daną textową. + +Edytuj Makefile i dodaj skrót języka do zmiennej `TRANSLATIONS`. Teraz możesz swoją pracę w każdej chwili sprawdzić: + + $ make tlh $ firefox book-tlh/index.html + +Używaj często 'commit' a gdy już skończysz, to daj znać. GitHub posiada interfejs, który to ułatwi: utwórz twój własny 'Fork' projektu "gitmagic", 'push' twoje zmiany i daj mi znać, by je 'mergen'. diff --git a/tmp/basic.txt b/tmp/basic.txt new file mode 100644 index 0000000..28aea7a --- /dev/null +++ b/tmp/basic.txt @@ -0,0 +1,195 @@ +== Pierwsze kroki == + +Zanim utoniemy w morzu poleceń Gita, przyjrzyjmy się najpierw kilku prostym poleceniom. Pomimo ich prostoty, wszystkie jednak są ważne i pożyteczne. W rzeczywistości, podczas pierwszych miesięcy pracy z Git nie wychodziłem poza zakres opisany w tym roźdźiale + +=== Zabezpieczenie obecnego stanu === + +Zamierzasz przeprowadzić jakieś drastychne zmiany? Zanim to zrobisz, zabezpiecz najpierw dane w aktualnym katalogu. + + $ git init + $ git add . + $ git commit -m "Mój pierwszy commit" + +Teraz, jeśli cokolwiek stałoby się z twymi plikami podczas edycji, możesz przywrócić pierwotną wersję: + + $ git reset --hard + +Aby zapisać nowy stan ponownie: + + $ git commit -a -m "Mój następny commit" + +=== Dodanie, kasowanie i zmiana nazwy === + +Powyższa komenda zatrzyma jedynie pliki, które już istniały podczas gdy poraz pierwszy wykonałes polecenie *git add*. Jeśli w międzyczasie dodałeś jakieś nowe pliki, Git musi zostać o tym poinformowany: + + $ git add readme.txt Dokumentacja + +To samo, gdy zechcesz by Git zapomniał o wybranych plikach: + + $ git rm ramsch.h archaiczne.c + $ git rm -r obciążający/materiał/ + +Jeśli sam tego jeszcze nie zrobiłeś, to Git usunie pliki za ciebie. + +Zmiana nazwy pliku, to to samo co jego skasowanie i ponowne utworzenie z nowa nazwa. Git wykorzystuje do tego skrót *git mv*, który posiada tą samą składnię co polecenie *mv*. Na przykład: + + $ git mv bug.c feature.c + +=== Zaawansowane anulowanie/przywracanie === + +Czasami zechcesz po prostu cofnać się w czasie, zapominając o wszystkich wprowadzonych od tego punktu zmianach. Wtedy: + + $ git log + +pokaże ci listę dotychczasowych 'commits' i ich kluczy SHA1: + +---------------------------------- +commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob +Date: Tue Mar 14 01:59:26 2000 -0800 + + Ersetzt printf() mit write(). + +commit 82f5ea346a2e651544956a8653c0f58dc151275c +Author: Alicja +Date: Thu Jan 1 00:00:00 1970 +0000 + + Initial commit. ---------------------------------- + +Kilka początkowych znaków klucza SHA1 wystarcza by jednoznacznie zidentyfikować 'commit', alternatywnie możesz skopiować i wkleić cały klucz. Wpisując: + + $ git reset --hard 766f + +przywrócisz stan do stanu podanego 'commit', a wszystkie poźniejsze zmiany zostaną bezpowrotnie skasowane. + +Innym razem chcesz tylko na moment przejść do jednego z poprzednich stanów. W tym wypadku użyj komendy: + + $ git checkout 82f5 + +Tym poleceniem wrócisz się w czasie zachowując nowsze zmiany. Ale, tak samo jak w podróżach w czasie z filmaów science-fiction - jeśli teraz dokonasz zmian i zapamietsz je poleceniem 'commit', zostaniesz przeniesiona do alternatywnej rzeczywostosci, ponieważ twoje zmiany różnią się od dokonanych w późniejszych punktach czasu. + +Tą alternatywną rzeczywistość nazywamy 'branch', a <>. Na razie, zapamietaj tylko, że: + + $ git checkout master + +sprowadzi cię znów do teraźniejszości. Również, aby uprzedzić narzekanie Gita, powinieneś przed każdym 'checkout' wykonać 'commit' lub 'reset'. + +Korzystając ponownie z analogii do gier komputerkowych: + +- *`git reset --hard`*: załadój jakiś starszy stan gry i skasuj wszystkie nowsze niż właśnie ładowany. + +- *`git checkout`*: Załadój stary stan, grając dalej, twój stan będzie się różnił od nowszych zapamietanych. Każdy stan, który zapamiętasz od teraz, powstanie w osobnym 'branch', reprezentującym alternatywną rzeczywitość. <> + +Jeśli chcesz, możesz przywrócić jedynie wybrane pliki lub katalogi poprzez dodanie ich nazw do polecenia + + $ git checkout 82f5 jeden.plik inny.plik + +Bądź ostrożny, ten sposób użycia komendy *checkout* może bez uprzedzenia skasować pliki. Aby zabezpieczyć sie przed takimi wypadkami powinieneś zawsze wykonać polecenie 'commit' zanim wykonasz 'checkout', szczególnie w okresie poznawania Gita. Jeśli czujesz się niepewnie przed wykonaniem jakiejś operacji Gita, generalną zasadą powinno stać sie dla ciebie uprzednie wykonanie *git commit -a*. + +Nie lubisz kopiować i wklejać kluczy SHA1? Możesz w tym wypadku skorzystać z: + + $ git checkout :/"Mój pierwszy c" + +by przenieś się do 'commit', którego opis rozpoczyna zawartą wiadomość. Możesz również cofnąć się do piątego z ostatnio zapamiętanych 'commit': + +$ git checkout master~5 + +=== Przywracanie === + +W sali sądowej pewne zdarzenia mogą zostać wykreślone z akt. Podobnie możesze zaznaczyć pewne 'commits' do wykreślenia. + + $ git commit -a + $ git revert 1b6d + +To polecenie wymaże 'commit' o wybranym kluczu. ten rewers zostanie zapamiętany jako nowy 'commit', co można sprawdzić poleceniem *git log*. + +=== Generowanie listy zmian === + +Niektóre projekty wymagają http://en.wikipedia.org/wiki/Changelog[pliku changelog]. Wygenerujesz go poleceniem: + + $ git log > Changelog + +=== Ładowanie plików === + +Kopię projektu zarządzanego za pomocą Gita uzyskasz poleceniem: + + $ git clone git://ścieżka/do/projektu + +By na przykład zładować wszystkie dane, których urzyłem do stworzenia tej strony skorzystaj z: + + $ git clone git://git.or.cz/gitmagic.git + +Do polecenia 'clone' wrócimy niebawem. + +=== Najnowszy stan === + +Jeśli posiadasz już kopię projektu wykonaną za pomocą *git clone*, możesz ją zaktualizować poleceniem: + + $ git pull + +=== Szybka publikacja === + +Przypóśćmy, że napisałeś skrypt i chcesz go udostępnić innym. Mogłabyś poprosić ich, by zładowali go bezpośrednio z twojego komputera. Jeśli jednak zrobią podczas gdy gdy ty wprowadzasz poprawki lub eksperymentujesz ze zmianami, mogłabyś przysporzyć im nieprzyjemności. Z tego powodu istnieje coś takiego jak cykl wydawniczy. Programiści regularnie pracują nad projektem, upubliczniają kod jednak dopiero, jeśli uznają, że nadaje sie już do pokazania. + +Aby wykonać to za pomocą GIT, wejdź do katalogu w którym znajduje się twój skrypt: + + $ git init + $ git add . + $ git commit -m "Pierwsze wydanie" + +Następnie poproś twych użytkowników o wykonanie: + + $ git clone twój.komputer:/ścieżka/do/skryptu + +by zładować twój skrypt. Zakładamy tu posiadanie przez nich klucza SSH do twojego komputera. Jeśli go nie mają, uruchom *git daemon* i podaj im następujący link: + + $ git clone git://twój.komputer/ścieżka/do/skryptu + +Od teraz, zawsze gdy uznasz, że wersja nadaje sie do opublikowania, wykonaj polecenie: + + $ git commit -a -m "Następna wersja" + +a twoi użytkownicy, po wejściu do katalogu zawierającym twój skrypt, będą go mogli zaktualizować poprzez: + + $ git pull + +Twoi użytkownicy nigdy nie wejdą w posiadanie wersji, których nie chcesz im udostępniać. + +=== A co robiłem ostatnio? === + +Jeśli chcesz zobaczyć zmiany, które wprowadziłeś of ostatniego 'commit', wpisz: + + $ git diff + +Albo tylko zmiany od wczoraj: + + $ git diff "@{yesterday}" + +Albo miedzy określoną wersją i dwoma poprzedzającymi: + + $ git diff 1b6d "master~2" + +Za każdym razem uzyskane informacje są równocześnie patchem, który poprzez *git apply* może zostac być zastosowany. Spróbuj również: + + $ git whatchanged --since="2 weeks ago" + +Jeśli chcę sprawdzić listę zmian jakiegoś repozytorium, często korzystam czesto z http://sourceforge.net/projects/qgit[qgit], ze względu na jego fotogeniczny interfejs, albo z http://jonas.nitro.dk/tig/[tig], tekstowy interfejs, działający zadowalająco gdy mamy do czynienia z wolnym łączem internetowym. Alternatywnie, zainstaluj serwer http, uruchom *git instaweb*, i odpal dowolną przeglądarkę internetową. + +=== Ćwiczenie === + +Niech A, B, C i D będą 4 nastepujacymi po sobie 'commits', gdzie B różni sie od A, jedynie tym, iż usunieto kilka plikow. Chcemy teraz te usuniete pliki zrekonstruowac do D. Jak to można zrobić? + +Istnieją przynajmniej 3 rozwiązania. Załóżmm że znajdujemy się obecnie w D: + +1. Różnica pomiędzy A i B, to skasowane pliki. Możemy utworzyc patch, który pokaże te różnice i nastepnie zastosować go: + + $ git diff B A | git apply + +2. Ponieważ pliki zostaly już raz zapamietane w A, możemy je przywrócić: + + $ git checkout A foo.c bar.h + +3. Możemy też widzieć przejście z A na B jako zmianę, którą można zrewertować: + + $ git revert B + +A które z tych rozwiązań jest najlepsze? To, które najbardziej tobie odpowiada. Korzystając z Git łatwo osiągnąć cel, czasami prowadzi do niego wiele dróg. diff --git a/tmp/branch.txt b/tmp/branch.txt new file mode 100644 index 0000000..d270759 --- /dev/null +++ b/tmp/branch.txt @@ -0,0 +1,190 @@ +== Magia branch == + +Szybkie, natychmiastowe działanie poleceń branch i merge, to jedne z najbardziej zabójczych właściwości Gita. + +*Problem*: Zewnetrzne faktory narzucają konieczność zmiany kontekstu. Poważne błędy w opublikowanej wersji ujawniły się bez ostrzeżenia. Skrócono termin opublikowania pewnej wlaściwości. Autor, którego pomocy potrzebujesz w jednej z kluczowych sekcji postanawia opóścić projekt. W wszystkich tych przypadkach musisz natychmiastowo zaprzestać bierzących prac i skoncentrować się nad zupełnie innymi zadaniami. + +Przerwanie toku myślenia nie jest dobre dla produktywności, a czym większa różnica w kontekście, tym wieksze straty. Używając centralnych systemów kontroli wersji musielibyśmy najpierw zładować świeżą kopię roboczą z serwera. W systemach rozproszonych wyglada to dużo lepiej, ponieważ możemy żądaną wersje skonowac lokalnie + +Jednak klonowanie równeż niesie za soba kopiowanie całego katalogu roboczego jak i całej historii projektu aż do żądanego punktu. Nawet jesli Git redukuje wielkość przez podział danych i użycie twardych dowiązań, wszystkie pliki projektu musza zostać odtworzone w nowym katalogu roboczym. + +*Rozwiazanie*: Git posiada lepsze narzędzia dla takich sytuacji, jest ono wiele szybsze i zajmujące mniej miejsca na dysku jak klonowanie: *git branch*. + +Tym magicznym słowem zmienisz dane w swoim katalogu roboczym z jednej wersji w inną. Ta przemiana potrafi dużo wiecej jak tylko poruszać się w historii projektu. Twoje pliki mogą przekształcić się z aktualnej wersji do wersji eksperymentalnej, do wersji testowej, do wersji twojego kolegi i tak dalej. + +=== Przycisk 'szef' === + +Być może grałes już kiedyś w grę, która posiadała magiczny (``przycisk szef''), po naciśnięciu którego twój monitor natychmiast pokazywał jakiś arkusz kalkulacyjny, czy coś tam? Celem przycisku było szybkie ukrycie gierki na wypadek pojawienia się szefa w twoim biurze. + +W jakimś katalogu: + + $ echo "Jestem mądrzejszy od szefa" > mój_plik.txt + $ git init + $ git add . + $ git commit -m "Pierwszy commit" + +Utworzyliśmy repozytorium Gita, które zawiera plik o powyższej zawartości. Następnie wpisujemy: + + $ git checkout -b szef # wydaje się, jakby nic się nie stało + $ echo "Mój szef jest ode mnie mądrzejszy" > mój_plik.txt + $ git commit -a -m "Druga wersja" + +Wygląda jakbyśmy zmienili zawartość pliku i wykonali 'commit'. Ale to iluzja. Wpisz: + + $ git checkout master # przejdź do orginalnej wersji + +i hokus-pokus! Poprzedni plik jest przywrócony do stanu pierwotnego. Gdyby jedmak szef zdecydował się grzebać w twoim katalogu, wpisz: + + $ git checkout szef # przejdź do wersji, która nadaje się do obejrzenia przez szefa + +Możesz zmieniać pomiedzy tymi wersjami pliku tak często jak zechcesz, każdą z tych wersji pliku możesz też niezależnie redagować. + +=== Brudna robota === + +[[branch]] +Zalozmy, pracujesz nad jakas funkcja, i musisz, jakiegokolwiek powodu, wrocic o 3 wersje wstecz w celu wprowadzenia kilku polecen print, aby sprawdzic jej działanie. Wtedy: + + $ git commit -a + $ git checkout HEAD~3 + +Teraz mozesz na dziko wprowadzać tymczasowy kod. Mozesz te zmiany nawet 'commit'. Po skończeniu, + + $ git checkout master + +wróci cię do poprzedniej pracy. Zauwaz, ze wszystkie zmiany, ktore nie zostaly zatwierdzone przez 'commit', zostaly przejete. + +A co jesli chciałeś zapamietac wprowadzone zmiany? Proste: + + $ git checkout -b brudy + +i tylko jeszcze wykonaj 'commit' zanim wrocisz do master branch. Jesli tylko chcesz wrocic do twojej brudnej roboty, wpisz po prostu + + $ git checkout brudy + +Spotkalismy sie z tym poleceniem juz we wczesniejszym rozdziale, gdy poruszalismy temat ladowania starych wersji. Teraz mozemy opowiedziec cala prawdę: pliki zmieniaja sie do żądanej wersji, jednak musimy opuscic master branch. Kazdy 'commit' od teraz prowadzi twoje dane inna droga, ktorej mozemy rowniez nadac nazwe. + +Innymi slowami, po przywolaniu ('checkout') starszego stanu Git automatychnie przenosi cię do nowego, nienazwanego brunch, ktory poleceniem *git checout -b* otrzyma nazwe i zostanie zapamietany. + +=== Szybka korekta bledow. === + +Będąc w środku jakiejś pracy, otrzymujesz polecenie zajecia sie nowo znaleziono bledem w 'commit' `1b6d...`: + + $ git commit -a + $ git checkout -b fixes 1b6d + +Po skorygowaniu błędu: + + $ git commit -a -m "Błąd usunięty" + $ git checkout master + +i kontynuujesz przerwana prace. Mozesz nawet ostatnio swiezo upieczona poprawkę przejac do aktualnej wersji: + + $ git merge fixes + +=== Merge === + +Za pomoca niektorych systemow kontroli wersji utworzenie nowego 'branch' moze i jest proste, jednak pozniejsze połączenie ('merge') skomplikowane. Z Gitem 'merge' jest tak trywialne, że możesz czasem nawet nie zostać powiadomiony o jego wykonaniu. + +W gruncie rzeczy spotkalismy sie juz wiele wczesniej z funkcją 'merge'. Polecenie *pull* ściąga inne wersje i je zespala ('merge') z twoim aktualnym 'branch'. Jesli nie wprowadziles zadnych lokalnych zmian, to 'merge' jest szybkim przejściem do przodu, jest to przypadek podobny do zładowania ostatniej wersji przy centralnych systemach kontroli wersji. Jesli jednak wprowadziles zmiany, Git automatycznie wykona 'merge' i powiadomi cie o eventualnych konfliktach. + +Zwyczajnie kazdy 'commit' posiada matczyny 'commit', a mianowicie poprzedzający go 'commit'. Zespolenie kilku 'branch' wytwarza 'commit' z minimum 2 matczynymi 'commit'. To nasuwa pytanie, ktory właścowie'commit' wskazuje na `HEAD~10`? Każdy 'commit' moze posiadac wiecej rodzicow, za którym właściwie podążamy? + +Wychodzi na to, ze ta notacja zawsze wybiera pierwszego rodzica. To jest wskazane, poniewaz aktualny 'branch' staje sie pierwszym rodzicem podczas 'merge', czesciej będziesz zainteresowany bardziej zmianami ktorych dokonales w aktualnym 'branch', niz w innych. + +Mozesz tez wybranego rodzica wskazać używając symbolu dzióbka. By na przyklad pokazac logi drugiego rodzica. + + $ git log HEAD^2 + +Mozesz pominac numer pierwszego rodzica. By na przyklad pokazac roznice z pierwszym rodzicem: + + $ git diff HEAD^ + +Mozesz ta notacje kombinowac takze z innymi rodzajami. Na przyklad: + + $ git checkout 1b6d^^2~10 -b archaiczne + +tworzy nowy 'branch' o nazwie '``archaiczne', reprezentujący stan 10 'commit' do tyłu drugiego rodzica dla pierwszego rodzica 'commit', ktorego hash rozpoczyna sie na 1b6d. + +=== Bezprzestojowy przebieg pracy === + +W procesie produkcji czesto drugi krok planu musi czekac na zakonczenie pierwszego. Popsuty samochod stoi w garazu nieuzywany do czasu dostarczenia czesci zamiennej. Prototyp musi czekac na wyprodukowanie jakiegś chipa zanim będzie mozna podjac dalsza konstrukcje. + +W projektach software moze to wygladac podobnie. Druga czesc jakiegos feature musi czekac, az pierwsza zostanie wydana i przetestowana. Niektore projekty wymagaja sprawdzenia twojego kodu zanim zostanie zaakceptoany, musisz wiec czekac, zanim pierwsza czesc zostanie sprawdzona, zanim bedziesz mogl zaczac z druga czescia + +Dzieki bezbolesnemu 'branch' i 'merge' mozemy te regoly naciagnac i pracowac nad druga czescia jeszcze zanim pierwsza zostanie oficjalnie zatwierdzona. Przyjmijmy, że wykonałeś 'commit' pierwszej części i przekazałeś do sprwadzenia. Przyjmijmy też, że znajdujesz sie w 'master branch'. Najpierw przejdź do 'branch'o nazwie czesc2: + +$ git checkout -b czesc2 + +Pracujesz w cześci 2 i regularnie wykonujesz 'commit'. Błądzenie jest ludzkie i może się zdażyć, że chcecie wrócić do części 1 i wprowadzić jakieś poprawki. Jeśli masz szczęście albo jesteś bardzo dobry, możesz ominąć następne linijki. + + $ git checkout master # przejdź do części 1 + $ fix_problem + $ git commit -a # zapisz rozwiązanie + $ git checkout czesc2 # przejdz do czesci 2 + $ git merge master # połącz zmiany + +Ewentualnie, część pierwsza zostaje dopuszczona: + + $ git checkout master # przejdź do części 1 + $ submit files # opublikuj twoja wersję + $ git merge czesc2 # Połącz z czescia 2 + $ git branch -d czesc2 # usuń branch czesc2 + +Znajdujesz się teraz spowrotem w master branch posiadając czesc2 w katalogu roboczym. + +Dość łatwo zastosować ten sam trik na dowolną ilość części. Równie łatwo można utworzyć 'branch' wstecznie: przypuśćmy, właśnie spostrzegłaś, iż już właściwie jakieś 7 'commit' wcześniej powinnaś stworzyć 'branch'. Wpisz wtedy: + + $ git branch -m master czesc2 # Zmień nazwę "master" na "czesc2". + $ git branch master HEAD~7 # utwórz ponownie "master" 7 'commits' do tyłu. + +Teraz `master` branch zawiera cześć 1 a branch `czesc2` zawiera całą resztę. Znajdujemy się teraz w tym ostatnim 'branch'; utworzyliśmy `master` bez wchodzenia do niego, gdyż zamierzamy dalszą pracę prowadzić w 'branch' `czesc2`. Nie jest to zbyt często stosowane. Do tej pory przechodziliśmy do nowego 'branch' zaraz po jego utworzeniu, tak jak w: + + $ git checkout HEAD~7 -b master # Utwórz branch i wejdź do niego. + +=== Reorganizacja składanki === + +Może lubisz odpracować wszystkie aspekty projektu w jednym 'branch'. Chcesz wszystkie bieżące zmiany zachować dla siebie, a wszyscy inni powinni zobaczyć twoje 'commit' po ich starannym zorganizowaniu. Wystartuj parę 'branch': + + $ git branch czyste # Utwórz branch dla oczyszczonych 'commits'. + $ git checkout -b zbieranina # utwórz 'branch' i przejdź do niego w celu dalszej pracy. + +Następnie wykonaj zamierzone prace: pousuwaj błędy, dodaj nowe funkcje, utwóż kod tymczasowy i tak dalej, regularnie wykonując 'commit'. Wtedy: + + $ git checkout czyste + $ git cherry-pick zbieranina^^ + +zastosuje najstarszy matczyny 'commit' z 'branch' ``zbieranina'' na 'branch' ``czyste''. Poprzez 'przebranie wisienek' możesz tak skonstruować 'branch', który posiada jedynie końcowy kod i zależne od niego pogrupowane 'commit'. + +=== Zarządzanie 'branch' === + +Listę wszystkich 'branch' otrzymasz poprzez: + + $ git branch + +Standardowo zaczynasz w 'branch' zwanym ``master''. Wielu opowiada się za pozostawieniem ``master'' branch w stanie dziewiczym i tworzeniu nowych dla twoich prac. + +Opcje *-d* und *-m* Optionen pozwalają na usuwanie i przesuwanie (zmianę nazwy) 'branch'. Zobacz: *git help branch*. + +Nazwa ``master'' jest bardzo użytecznym stworem. Inni mogą wychodzić z założenia, że twoje repozytorium takowy posiada i że zawiera on oficjalną wersję projektu. Nawet jeśli mógłbyś skasować lub zmienić nazwę na inną powiniaś respektować tę konwencję. + +=== Tymczasowe 'branch' === + +Po jakimś czasie zapewne zauważysz, że często tworzysz 'branch' o krótkiej żywotności, w wiekszości z tego samego powodu: każdy nowy 'branch' służy jedynie do tego, by zabezpieczyć aktualny stan,, aby móc wrócić do jednego z poprzednich punktów i poprawić jakieś priorytetowe błędy czy cokolwiek innego. + +Można to porównać do chwilowego przełączenia kanału telewizyjnego, by sprawdzić co dzieje się na innym. Lecz zamiast naciskać guziki pilota, korzystasz z poleceń 'create', 'checkout', 'merge' i 'delete'. Na szczęście Git posiada na te operacje skrót, który jest tak samo komfortowy jak pilot telewizora: + + $ git stash + +Polecenie to zabezpiecza aktualny stan w tymczasowym miejscu ('stash' = ukryj) i przywraca poprzedni stan. Twój katalog roboczy wygląda dokładnie tak, jak wyglądał zanim zacząłeś edycję. Teraz możesz poprawiać błędy, zładować zmiany z centralnego repozytorium ('pull') i tak dalej. Jeśli chcesz powrócić spowrotem do ukrytego ('stashed') stanu, wpisz: + + $ git stash apply # Prawdopodobnie będziesz musiał rozwiązać konflikty. + +Możesz posiadać więcej 'stash'-ów i traktować je w zupełnie inny sposób. Zobacz *git help stash*. Jak już prawdopodobnie się domyślasz, Git korzysta przy wykonywaniu tej magicznej sztuczki z funkcji 'branch' w tle. + +=== Pracuj jak chcesz === + +Może pytasz się, czy 'branch' są warte tego zachodu. Jakby nie było, polecenia 'clone' są prawie tak samo szybkie i możesz po prostu poleceniem *cd* zmieniać pomiędzy nimi, bez stosownia ezoterycznych poleceń Gita. + +Przyjżyjmy się takiej przeglądarce internetowej. Dlaczego pozwala używać tabów tak samo jak i nowych okien? Ponieważ udostępnienie obu możliwości pozwala na stosowanie wielu stylów. Niektórzy użytkownicy preferują otwarcie jednego okna przeglądarki i korzystają z tabów dla wyświetlenia różnych stron. Inni upierają się przy stosowaniu pojedyńczych okien dla każdej strony,zupełnie bez korzystania z tabów. Jeszcze inni znowu wolą coś pomiędzy. + +Stosowanie 'branch' to jak korzystanie tabów dla twojego katalogu roboczego, a klonowanie porównać można do otwarcia wielu okien przeglądarki. Obie operacje są szybkie i lokalne, dlaczego nie poeksperymentować i nie znaleźć dla siebie najbardziej odpowiedniej kombinacji. Git pozwoli ci pracować dokładnie tak jak chcesz. diff --git a/tmp/clone.txt b/tmp/clone.txt new file mode 100644 index 0000000..6cb74c5 --- /dev/null +++ b/tmp/clone.txt @@ -0,0 +1,194 @@ +== Klonowanie == + +W starszych systemach kontroli wersji polecenie 'checkout' stanowi standardową operacje pozyskiwania danych. Otrzymasz zbiór plików konkretnegj wersji. + +W Git i innych rozproszonych systemach kontroli wersji to operacja 'clone' jest standardem. By pozyskać dane, tworzysz klon całego repozytorium. Lub inaczej mówiąc, odzwierciedlasz centralny server. Wszystko, co można zrobić w centralnym repozytorium, możesz również robić z klonem. + +=== Synchronizacja komputera === + +Dla celów ochrony danych mógłbym jeszcze zaakceptować korzystanie z archiwów tar, a dla prostej synchonizacji używania *rsync*. Jednak czasami pracując na laptopie,innym razem na desktopie, może zdarzyć się, że nie miały możliwości w międzyczasie się zsynchronizować. + +Utwóż repozytorium Gita w wykonaj 'commit' twoich danych na jednym komputerze. Potem na tym następnym wpisz: + + $ git clone drugi.komputer:/ścieżka/do/danych + +by otrzymać drugą kopie danych i jednocześnie utworzyć repozytorium Gita. Od teraz poleceniem: + + $ git commit -a + $ git pull drugi.komputer:/ścieżka/do/danych HEAD + +przenosisz stan drugiego komputera na komputer na którym właśnie pracujesz. Jeśli dokonałeś zmian w tym samym pliku na obydwu komputerach, Git cię o tym poinformuje, po usunięciu konfliktu powinieneś ponowić 'commit'. + +=== Klasyczna kontrola kodu źródłowego === + +Utwóż repozytorium Gita twoich danych + + $ git init + $ git add . + $ git commit -m "Pierwszy commit" + +Na centralnym serwerze utwóż tzw 'bare repository' w jakimkolwiek katalogu: + + $ mkdir proj.git + $ cd proj.git + $ git --bare init + $ touch proj.git/git-daemon-export-ok + +W razie konieczności, wysartuj daemon Gita: + + $ git daemon --detach # ponieważ, mógłby już lecieć + +Jeśli korzystasz z hostingu to poszukaj wskazówek utwożenia pustego repozytorium. Zwykle konieczne jest do tego wypełnienie formulaża online na stronie internetowej usługodawcy. + +Przenieś ('push') twój projekt teraz na centralny serwer: + + $ git push centralny.serwer/ścieżka/do/projektu.git HEAD + +By pozyskać kod źródłowy programista podaje zwykle polecenie tego rodzaju: + + $ git clone centralny.serwer/ścieżka/do/projektu.git + +Po dokonaniu edycji programista zapamiętuje zmiany najpierw lokalnie: + + $ git commit -a + +Aby zaktualizować do wersji na serwerze: + + $ git pull + +Jeżli wystąpią jakiekolwiek konflikty 'merge', powinny być usunięte in na nowo wykonany 'commit'. + + $ git commit -a + +Lokalne zmiany przekazujemy do serwera poleceniem: + + $ git push + +Jeśli w międzyczasie nastąpiły nowe zmiany na serwerze wprowadzone przez innego programistę, twój 'push' nie powiedzie się. Zaktualizuj lokalne repozytorium ponownie poleceniem 'pull', pozbądź się konfliktów i spróbuj jeszcze raz. + +Programiści potrzebują dostępu poprzez SSH by móc wykonać polecenia 'pull' i 'push'. Mimo to każdy może otrzymać kod źródłowy poprzez podanie: + + $ git clone git://centralny.serwer/ścieżka/do/projektu.git + +Protokół Git przypomina HTTP: nie posiada autentyfikacji, więc każdy może zładować dane. Przy ustawieniach standardowych wykonanie 'push' za pomocą protokołu 'git' jest zdeaktywowane. + +=== Utajnione Źródło === + +Przy projektach Closed-Source wyklucz używanie poleceń jak clone i upewnij się, że nigdy nie został utworzony plik o nazwie git-daemon-export-ok. Wtedy repozytorium nie może już komunikować sie poprzez protokół 'git', tylko posiadający dostęp przez SSH mogą widzieć dane. Jeśli wszystkie REPOSITORIES są zamknięte, nie ma potrzeby startować deamon GIT, ponieważ cała komunikacja odbywa się za pomocą SSH. + +=== Gołe repozytoria === + +Gołe ('bare') repozytorium zostało tak nazwane, ponieważ nie posiada katalogu roboczego. Posiada jedynie dane, które są zwykle schowane w podkatalogu .git. Innymi słowy, zarządza historią projektum, nie otrzymuje jednak odzwierciedlenia katalogu roboczego jakiejkolwiek wersji. + +Repozytorium 'bare' przejmuje rolę podobną do roli głównego serwera w scentralizowanych systemach kontroli wersji: dom twojego projektu. Programiści klonują twój projekt stamtąd i przesyłają tam ostatnie oficjalne zmiany. Często znajduje się ono na serwerze, który nie robi dużo więcej, niż rozpowszechnianie danych. Sama praca dzieje się w klonach projektu, w ten sposób główne repozytorium daje sobie radę nie korzystając z katalogu roboczego. + +Wiele z poleceń Gita nie będzie funkcjonować w repozytoriach 'bare', chyba że ustawimy zmienną systemową GIT_DIR na katalog roboczy repozytorium albo przekażemy opcję `--bare`. + +=== 'Push', czy 'pull' === + +Dlaczego wprowadziliśmy polecenie 'push', i nie pozostaliśmy przy znanym nam już 'pull'? Po pierwsze, 'pull' nie działa z repozytoriami 'bare': zamiast niego używaj 'fetch', polecenia którym zajmiemy się później. Również gdybyśmy nawet używali normalnego repozytorium na serwerze centralnym, polecenie 'pull' byłoby raczej niewygodne. Musielibyśmy wpierw zalogować się na serwerze i przekazać poleceniu 'pull' adres IP komputera z którego chcemy ściągnąć pliki. Jeśli wogóle posiadalibyśmy dostęp do konsoli serwera, to prawdopodobnie przeszkodziłaby nam firewall po drodze do naszego komputera. + +W każdym bądź razie, nawet jeśli by to działało, odradzamy z korzystania z 'push' w ten sposób. Poprzez samo posiadanie katalogu roboczego na serwerze mogłoby powstać wiele nieścisłości. + +Krótko mówiąc, podczas gdy uczysz się korzystania z Git, korzystaj z polecenia 'push' tylko, gdy celem jest jest repozytorim 'bare', w wszystkich innych wypadkach z 'pull'. + +=== Rozwidlenie projektu === + +Jeśli nie potrafisz już patrzeć na kierunek rozwoju w którym poszedł jakiś projekt. Uważasz, że potrafisz to lepiej. To po prostu zrób wykonaj na twoim serwerze: + + $ git clone git://główny.serwer/ścieżka/do/danych + +Następnie, poinformuj wszystkich o nowym 'fork' projektu na twoim serwerze. + +W każdej późniejszej chwili możesz wykonać 'merge' zmian oryginalnego projektu, poprzez: + + $ git pull + +=== Ultymatywny backup danych === + +Chcesz posiadać liczne, wolne od manipulacji, redudantne kopie bezpieczeństwa w różnych miejscach? Jeśli projekt posiada wielu programistów, nie musisz niczego robić. Ponieważ każdy klon twojego kodu jest pełnowartościową kopią bezpieczeństwa. Nie tylko jego aktualny stan, lecz również cała jego historia. Gdy jakikolwiek klon zostanie uszkodzony, dzięki kryptograficznemu hashowaniu, zostanie to natychmiast rozpoznane, gdy tylko ta osoba będzie próbować wymiany z innymi. + +Jeśli twój projekt nie jest jeszcze wystarczająco znany, spróbuj pozyskać tak wiele serwerów ile to możliwe, by umieścić tam jego klon. + +Ci najbardziej paranoidalni powinni zawsze zapisywać 20 bajtów ostatniego klucza SHA1 z HEAD i przechowywać w bezpiecznym miejscu. Musi być bezpieczny, jednak nie tajny. Na przykład opublikowanie go w gazecie dobrze by spełniło swoje zadanie, byłoby dość trudnym zadaniem zmanipulować każdą kopię gazety. + +=== Multitasking z prędkością światła === + +Załóżmy, że chcesz pracować nad kilkoma funkcjami równocześnie. Wykonaj 'commit' i wpisz: + + $ git clone . /jakiś/nowy/katalog + +Za sprawą http://en.wikipedia.org/wiki/Hard_link[twardych linków], stworzenie lokalnego klonu zajmuje dużo mniej czasu i pamięci niż zwykły backup. + +Możesz pracować nad dwoma niezależnymi funkcjami jednocześnie. Na przykład, możesz pracować nad jednym klonem dalej, podczas gdy drugi jest właśnie kompilowany. W każdym momencie możesz wykonać 'commit' i 'pull' innego klonu. + + $ git pull /inny/klon HEAD + +=== Kontrola wersji z podziemia === + +Pracujesz nad projektem, który używa innego systemu kontroli wersji i tęsknisz za Gitem? Utwórz po prostu repozytorium Gita w twoim katalogu roboczym: + + $ git init + $ git add . + $ git commit -m "Pierwszy commit" + +następnie sklonuj go: + + $ git clone . /jakiś/inny/katalog + +Przejdź teraz do nowego katalogu i pracuj według upodobania. Kiedyś zechcesz zsynchronizować pracę, idź do orginalnego katakogu, zaktualizuj go najpierw z tym innym systemem kontroli wersji, następnie wpisz: + + $ git add . + $ git add . $ git commit -m"Synchronizacja z innym systemem kontroli wersji" + +Teraz przejdź do nowego katalogu i podaj: + + $ git commit -a -m "Opis zmian" + $ git pull + +Sposób w jaki przekaższ zmiany drugiemu systemowi zależy już od jego sposobu działania. Twoj nowy katalog posiada dane z przez ciebie wprowadzonymi zmianami. Wykonaj konieczne kroki opisane w innym systemie, by przekazać dane do centralnego składu. + +Subversion, być może najlepszy z centralnych systemów, stosowany jest w wielu projektach. Komenda *git svn* automatyzuje powyższe kroki, może być użyta na przykład do http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[exportu projektu Git do repozytorium Subversion]. + +=== Mercurial === + +Mercurial to podobny system kontroli wersji, który prawie bezproblemowo potrafi pracować z Gitem. Korzystając z rozszerzenia `hg-git` użytkownik Mercurial jest w stanie prawie bez strat wykkonywać 'push' i 'pull' z repozytorium Gita. + +Sciągnij sobie rozszerzenie `hg-git` za pomocą Gita: + + $ git clone git://github.com/schacon/hg-git.git + +albo za pomocą Mercurial: + + $ hg clone http://bitbucket.org/durin42/hg-git/ + +Niestety nie są mi znane takie rozszerzenia dla Gita. Dlatego jestem za używaniem Gita jako centralnegoo składu, nawet gdy preferujesz Mercurial. W projektach prowadzonych za pomocą Mercurial często znajdziemy woluntariusza, który równolegle prowadzi skład Gita, do którego dzięki pomocy rozszerzenia `hg-git` projekty Gita automatycznie osiągają użytkowników Mercurial. + +To rozszerzenie potrafi również zmienić skład Mercurial w skład Gita, za pomocą komendy 'push' do pustego składu Gita. Jeszcze łatwiej dokonamy tego skryptem `hg-fast-export.sh`, który możemy tu znaleźć: + + $ git clone git://repo.or.cz/fast-export.git + +Aby przekonwertować, wejdź do pustego katalogu: + + $ git init + $ hg-fast-export.sh -r /hg/repo + +po uprzednim dodaniu skryptu do twojego `$ PATH`. + +=== Bazaar === + +Wspomnijmy również pokrótce o Bazaar, ponieważ jest to najbardziej popularny darmowy rozproszony system kontroli wersji po Git i Mercurial. + +Bazar będąc stosunkowo młodym systemem, posiada zaletę perspektywy czasu, jego twórcy mogli uczyć się na błędach z przeszłości i uniknąć historycznych naleciałości. Pozatym programiści byli świadomi popularności i wagi interakcji z innymi systemami kontroli wersji. + +Rozszerzenie `Bzr-git` pozwala użytkownikom Bazar dość łatwo pracować z repozytoriami Gita. Program `tailor` konwertuje składy Bazaar do składów Git i może robić to na bieżąco, podczas gdy `bzr-fast-export` lepiej nadaje się do jednorazowej konwersji. + +=== Dlaczego korzystam z GIT === + +Zdecydowałem się pierwotnie do wyboru GIT, ponieważ słyszałem, że jest w stanie zarządzać tak zawiłym i rozległym projektem jak kod źródłowy Linuxa. Jak na razie nie miałem powodów do zmiany. Git służył mi znakomicie i jak na razie jeszcze mnie nie zawiódł. Ponieważ w pierwszej lini pracuje na linuksie, problemy innych platform nie mają dla mnie znaczenia. + +Preferuję również programy C i skrypty bash w opozycji do na przykład Pythona: posiadają mniej zależności, i wolę też, gdy kod jest wykonywany szybko. + +Myślałem już też nad tym, jak można by ulepszyć Gita, poszło to nawet tak daleko, że napisałem własną aplikacje podobną do niego, w celu jednak wyłącznie ćwiczeń akademickich. Nawet gdybym zakończył mój projekt, mimo to pozostałbym przy Git, bo ulepszenia byłyby zbyt minimalne by uzasadnić zastosowanie odosobnionego systemu. + +Oczywiście może się okazać, że twoje potrzeby i oczekiwania są zupełnie inne i być może wygodniej jest tobie z zupełnie innym systemem. Jakby jednak nie spojrzeć, stosując Git nie popełnisz nic złego. diff --git a/tmp/drawbacks.txt b/tmp/drawbacks.txt new file mode 100644 index 0000000..d2d74d2 --- /dev/null +++ b/tmp/drawbacks.txt @@ -0,0 +1,91 @@ +== Załącznik A: Niedociągnięcia Gita == + +O kilku problemach mogących wystąpić z GIT nie wspomniałem do tej pory. Niektóre z nich można łatwo rozwiązać korzystając ze skryptów i 'Hooks', inne wymagają reorganizacji i ponownego zdefiniowania całego projektu, a na rozwiązanie kilku innych uniedogodnień możesz tylko uzbroić się w cierpliwość i czekać na ich rozwiązanie. Albo jeszcze lepiej, samemu się nimi zająć i spróbować pomóc. + +=== Słabości SHA1 === + +Z biegiem czasu kryptografowie odkrywają coraz więcej słabości systemu SHA1. Już dzisiaj byłoby możliwe dla przeciębiostw dysponujących odpowiednimi zosobami finansowymi znaleźć kolizje w hash-ach Za kilka lat możliwe, że całkiem normalny domowy PC będzie dysponował odpowiednim zasobem mocy obliczeniiowej, by skorumpować niepostrzeżenie repozytorium Gita. + +Miejmy nadzieję, że Git przestawi sie na lepszą funkcje hashującą, zanim badania nad SHA1 zupełnie zrobią go bezużytecznym. + +=== Microsoft Windows === + +Korzystanie z Gita pod Microsoft Windows może być frustrujące: + +- http://cygwin.com/[Cygwin], unixoidalne środowisko dla Windowsa posiada http://cygwin.com/packages/git/[port Gita]. + +- http://code.google.com/p/msysgit/[Git dla MSys] jest jedną z alternatyw, niektóre polecenia wymagają jednak poprawek. + +=== Pliki niepowiązane === + +Jeśli twój projekt jest bardzo duży i zawiera wiele plików, które nie są bezpośrednio ze sobą związane, mimo to jednak często zostają zmieniane, Git może tu działać gorzej niż innye systemy, ponieważ nie prowadzi monitoringu poszczególnych plików. Git kontrojuje zawsze całość projektu, co w normalnym wypadku jest zaletą. + +Jednym z możliwych rozwiązań mogłoby być podzielenie twojego projektu na kilka mniejszych, w których znajdują się jedynie pliki od siebie zależne. Korzystaj z *git submodule* jeśli mimo to chcesz cały twój projekt mieć w tym samym repozytorium. + +=== Kto nad czym pracuje? === + +Niektóre systemy kontroli wersji zmuszają cię, by w jakiś sposób oznaczyć pliki nad którymi pracujesz. Mimo że jest to bardzo uciążliwe, gdyż wymaga ciągłej komunikacji z serwerem centralnym, posiada to też swoje zalety: + +1. Różnice zostają szybko znalezione, ponieważ wystarczy skontrolować wyłącznie oznaczone pliki. + +2. Każdy może szybko sprawdzić, kto aktualnie nad czym pracuje, sprawdzając na serwerze po prostu kto zaznaczył jakie dane do edycji. + +Używając odpowiednich skryptów uda ci się to również przy pomocy Gita. Wymaga to jednak współdziałania programistów, ponieważ muszą również korzystać z tych skryptów podczas pracy nad plikiem. + +=== Historia pliku === + +Pomieważ Git loguje zmiany tylko dla całości projektu jako takiego, rekonstrukcja przebiegu zmian pojedyńczego pliku jest bardziej pracochłonna, niż w innych systemach, które kontrolują pojedyńcze pliki. + +Te wady są w wieszości przypadków marginalne i nie są brane pod uwagę, ponieważ inne operacje są bardzo wydajne. Na przykład polecenie `git checkout` jest szybsze niż `cp -a`, zmiany w zakresie całego projektu daje się lepiej komprymować niż zbiór zmian na bazie pojedyńczych plików. + +=== Pierwszy klon === + +Wykonanie klonu jest kosztowniejsze niż w innych systemach kontrli wersji jeśli istnieje długa historia. + +Początkowy koszt spłaca sie jednak na dłuższą metę ponieważ większość przyszłych operacji przeprowadzane będzie szybko i offline. Niemniej jednak istnieją sytuacje, w których lepiej utworzyć powierzchowny klon korzystając z opcji `--depth`. Trwa to o wiele krócej, taki klon taki posiada też tylko ograniczoną funkcjonalność. + +=== Niestałe projekty === + +Git został napisany z myślą optymalizacji prędkości działania przy dokonywaniu wielkich zmian. Ludzie robią jednak pomniejsze zmiany z wersji na wersję. Jakaś poprawka tutaj, jakaś nowa funkcja gdzie indziej, poprawienie komentarzy itd. Ale jeśli twoje dane znacznie się od siebie różnią pomiędzy następującymi po sobie wersjami, to chcąc nie chcąc przy każdym 'commit' projekt zwiększy się o twoje zmiany. + +Nie wymyślono jednak do tej pory niczego w żadnym systemie kontroli wersji, by móc temu zapobiec, tutaj jednak użytkownik Gita cierpi najbardziej, ponieważ w normalnym wypadku klonuje cały przebieg projektu. + +Powinno się w takim wypadku szukać powodów wystąpienia największych zmian. Ewentualnie można czasami zmienić format danych. Małe zmiany w projekcie powinny pociągać tylko minimalne zmiany na tak wąskiej grupie plików, jak to tylko możliwe. + +Może czasami bardziej wskazana byłaby baza danych, czy jakiś system archiwizacji zamiast systemu kontroli wersji. Na przykład nie jest dobrym sposobem zastosowanie systemu kontroli wersji do zarządzania zdjęciami wykonywanymi periodycznie przez kamerę internetową. + +Jeśli dane ulegają ciągłym zmianom i naprawdę muszą być wersjonowane, jedną z możliwości jest zastosowanie Git w scentralizowanej formie. Każdy może dokonywać pobierznych klonów, które mało co lub wcale nie mają nic do czynienia z przebiegiem projektu. Oczywiście w takim wypadku wiele funkcji Gita nie bedzie dostępnych a zmiany muszą być przekazywane w formie 'patch'. Prawdopodobnie będzie to dość dobrze działać, mimo iż nie jest do końca jasne komu potrzebna jest znajomość przebiegu tak ogromnej ilości niestabilnych danych. + +Innym przykładem może być projekt, który zależny jest od firmware przyjmującej kształt wielkiej danej w formie binarej. Historia pliku firmware nie interesuje użytkownika, a zmiany nie pozwalają sie wygodnie komprymować, wielkość repozytorium wzrasta niepotrzebnie o nowe wersje binarnego pliku firmware. + +W takim wypadku należałoby trzymać w repozytorium wyłącznie kod źródłowy, a sam plik binarny poza nim. By ułatwić sobie życie, ktoś mógłby opracować skrypt, który Git wykorzystuje do klonowania kodu źródłowego i 'rsync' albo pobieżny klon dla samego firmware. + +=== Licznik globalny === + +Wiele systemów kontroli wersji udostępnia licznik, który jest zwiększany z każdym "commit". Git natomiast odwołuje się przy zmianach do hasha SHA1, który w wielu przypadkach jest lepszym rozwiązaniem. + +Niektórzy jednak przyzwyczaili się do tego licznika. Na szczęście, łatwo jest pisać skrypty, zwiększające stan licznika przy każdej aktualizacji centralnego repozytorium Gita. Może jako forma taga, który powiązany jest z hashem SHA1 ostatniego 'commit'. + +Każdy klon mógłby posiadać taki licznik, jednak byłby on prawdopodobnie bezużyteczny, ponieważ tylko licznik centralnego repozytoriom ma znaczenie. + +=== Puste katalogi === + +Nie ma możliwości wersjonowania pustych katalogów. Aby obejść ten problem wystarczy utworzyć w takim katalogu plik dummy. + +To raczej obecna implementacja Gita, a mniej jego konstrukcja, jest odpowiedzialna za to wadę. Przy odrobinie szczęścia, jeśli Git jeszcze bardziej sie upowszechni i więcej użytkowników żądać będzie tej funkcji, to być może zostanie zaimplementowana. + +=== Pierwszy 'commit' === + +Stereotypowegy informatyk liczy od 0 zamiast 1. Niestety, w kwestii 'commits' Git nie podąża za tą konwencją. Wiele komend zachowuje sie zgrzędliwie przed wykonaniem pierwszego 'commit'. Dodatkowo, różnego rodzaju krańcowe przypadki muszą być traktowane specjalnie, jak 'rebase' dla 'branch' o różniącym sie pierwszym 'commit'. + +Git zyskałby na zdefiniowaniu tzw 0-'commit' zaraz po zainicjowaniu repozytorium, 'HEAD' zostałby ustawiony na 20 bajtow SHA1. Ten specjalny 'commit' reprezentowałby puste drzewo, bez rodziców, być może pradziad wszystkich repozytorii. + +Jeśli na przykład użytkownik wykonałby polecenie *git log*, zostałby poinformowany, że nie istnieje jeszcze żaden 'commit', gdzie na dzień dzisiejszy taka komenda wywoła błąd. Analogicznie dzieje się też z innymi poleceniami. + +Każdy inicjujący 'commit' byłby pochodną tego zerowego 'commit'. + +Niestety występuje jeszcze kilka innych problemów. Jeśli chcemy scalić kilka 'branches' o różniących sie inicjalnych 'commits' i przeprowadzić 'rebase', musimy ręcznie ingerować. + +=== Charakterystyka zastosowania === + +Dla 'commits' A i B, znaczenie wyrażeń "A..B" i "A...B" zależy od tego, czy polecenie oczekuje dwóch punktów końcowych, czy zakresu. Sprawdź *git help diff* i *git help rev-parse*. diff --git a/tmp/grandmaster.txt b/tmp/grandmaster.txt new file mode 100644 index 0000000..23f0d00 --- /dev/null +++ b/tmp/grandmaster.txt @@ -0,0 +1,179 @@ +== Git dla zaawansowanych == + +W międzyczasie powinnaś umieć odnaleźć się na stronach *git help* i rozumieć większość zagadnień. Mimo to może okazać się dość mozolne odnalezienie odpowiedniej komendy dla rozwiązania pewnych zadań. Może uda mi się zaoszczędzić ci trochę czasu: poniżej znajdziesz kilka recept, które były mi przydatne w przeszłości. + +=== Publikowanie kodu źródłowego === + +Git zarządza w moich projektach dokładnie tymi danymi, które chcę archiwizować i dać do dyspozycji innym użytkownikom. Aby utworzyć archiwum tar kodu źródłowego, używam polecenia + + $ git archive --format=tar --prefix=proj-1.2.3/ HEAD + +=== Zmiany dla 'commit' === + +Powiadomienie Gita o dodaniu, skasowaniu czy zmianie nazwy plików może okazać sie przy niektórych projektach dość uciążliwą pracą. Zamiast tego można skorzystać z: + + $ git add . + $ git add -u + +Git przyjży się danym w aktualnym katalogu i odpracuje sam szczegóły. Zamiast tego drugiego polecenia możemy użyć `git commit -a`, jeśli i tak mamy zamiar przeprowadzić 'comit'. Sprawdź też *git help ignore*, by dowiedzieć się jak zdefiniować dane, które powinny być zignorowane. + +Można to także wykonać za jednyym zamachem: + + $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove + +Opcje *-z* i *-0* zapobiegą przed niechcianymi efektmi ubocznymi przez niestandardowe znaki w nazwach plików. Ale ponieważ to polecenie dodaje również pliki które powinny być zignorowane, można dodać do niego jeszcze opcje `-x` albo `-X` + +=== Mój 'commit' jest za duży! === + +Od dłuższego czasu nie pamiętałeś o wykonaniu 'commit'? Tak namiętnie programowałeś, że zupełnie zapomniałeś o kontroli kodu źródłowego? Przeprowadzasz serię niezależnych zmian, bo jest to w twoim stylu? + +Nie ma sprawy, wpisz polecenie: + + $ git add -p + +Dla każdej zmiany, której dokonałeś Git pokaże ci pasaże z kodem, który uległ zmianom i spyta cię, czy mają zostać częścią następnego 'commit'. Odpowiedz po prostu "y" dla tak, albo "n" dla nie. Dysponujesz jeszcze innymi opcjami, na przykład dla odłożenie w czasie decyzji, wpisz "?", by dowiedzieć się więcej. + +Jeśli jesteś już zadowolony z wyniku, wpisz: + + $ git commit + +by dokonać wybranych zmian. Uważaj tylko, by nie skorzystać z opcji *-a*, ponieważ wtedy git dokona 'commit' zawierający wszystkie zmiany. + +A co, jeśli pracowałeś nad wieloma danymi w wielu różnych miejscach? Sprawdzenie każdej danej z osobna jest frustrujące i męczące zarówno. W takim wypadku skorzystaj z *git add -i*, obsługa tego polecenia może nie jest zbyt łatwa, za to jednak bardzo elastyczna. Kilkoma naciśnięciami klawiszy możesz wiele zmienionych plików dodać ('stage') albo usunąć z 'commit' ('unstage'), jak również sprawdzić, czy dodać zmiany dla poszczególnych plików. Alternaywnie możesz skorzystać z *git commit \--interactive*, polecenie to wykona automatycznie 'commit' gdy skończysz. + +=== Index: rusztowanie Gita === + +Do tej pory staraliśmy się omijać sławny 'index', jednak przyszedł czas się nim zająć, aby móc wyjaśnić wszystko to co poznaliśmy do tej pory. Index jest tymczasowym rusztowaniem Git rzadko wymienia dane bezpośrednio między twoim projektem a swoją historią wersji. Raczej zapisuje on dane najpierw w indexie, dopiero po tym kopiuje dane z indexu na ich właściwe miejsce przeznaczenia. + +Na przykład polecenie *commit -a* jest właściwie procesem dwustopniowym. Pierwszy krok to stworzenie obrazu bieżącego statusu każdego monitorowanego pliku do indeksu. Drugim krokiem jest trwałe zapamiętanie obrazu do indeksu. Wykonanie 'commit' bez opcji *-a* wykona jedynie drugi wspomniany krok i ma jedynie sens, jeśli poprzednio wykonano komendę, która dokonała odpowiednich zmian w indexie, na przykład *git add*. + +Normalnie możemy ignorować indeks i udawać, że czytamy i zapisujemy bezpośrednio z historii. W tym wypadku chcemy posiadać jednak większą kontrolę, więc manipulujemy indeks. Tworzymy obraz niektórych, jednak nie wszystkich zmian w indeksie i zapamiętujemy trwale starannie dobrany obraz. + +=== Nie trać głowy === + +Identyfikator 'HEAD' zachowuje się jak kursor, który zwykle wskazuje na najmłodszy 'commit' i z każdym nowym 'commit' zostaje przesunięty do przodu. Niektóre komendy Gita pozwolą ci nim manipulować. Na przyklad: + + $ git reset HEAD~3 + +przesunie identyfikator 'HEAD' o 3 'commits' spowrotem. Spowoduje to, że wszystkie następne komendy Gita będą reagować, jakby tych trzech ostatnich 'commits' wogóle nie było, podczas gdy twoje dane zostaną w przyszłości. Na stronach pomocy Gita znajdziesz więcej takich zastosowań. + +Ale jak teraz wrócić znów do przyszłości? Poprzednie 'commits' nic nie wiedzą o jej istnieniu. + +Jeśli posiadasz klucz SHA1 orginalnego 'HEAD', wtedy możesz wrócić komendą: + + $ git reset 1b6d + +Wyobraź jednak sobie, że nigdy go nie notowałeś? Nie ma sprawy: Przy wykonywaniu takich poleceń Git archiwizuje orginalny HEAD jako indentyfikator o nazwie ORIG_HEAD a ty możesz bezproblemowo wrócić używając: + + $ git reset ORIG_HEAD + +=== Łowcy głów === + +Może się zdarzyć, że ORIG_HEAD nie wystarczy. Może właśnie spostrzegłeś, iż dokonałeś kapitalnego błędu i musisz wrócić się do bardzo starego 'commit' w zapomnianym 'branch'. + +Standardowo Git zapamiętuje 'commit' przez przynajmniej 2 tygodnie, nawet jeśli poleciłeś zniszczyć 'branch' w którym istniał. Problemem staje się tutaj odnalezienie odpowieniego klucza SHA1. Możesz po kolei testować wszystkie klucze SHA1 w `.git/objects` i w ten sposób próbować odnaleźć szukany 'commit'. Istnieje jednak na to dużo prostszy sposób. + +Git zapamiętuje każdy obliczony klucz SHA1 dla odpowiednich 'commit' w `.git/logs`. Podkatalog `refs` zawieza przebieg wszystkich aktywności we wszystkich 'branches', podczas gdy plik `HEAD` wszystkie klucze SHA1 które kiedykolwiek posiadał. Ostatnie możemy zastosować do odnalezienia kluczy SHA1 tych 'commits' które znajdowały się w nieuważnie usuniętym 'branch'. + +Polecenie 'reflog' daje nam do dyspozycji przyjazny interfejs do tych właśnie logów. Wypróbuj polecenie: + + $ git reflog + +Zamiast kopiować i wklejać klucze z 'reflog', możesz: + + $ git checkout "@{10 minutes ago}" + +Albo przywołaj 5 z ostatnio oddwiedzanych 'commits' za pomocą: + +$ git checkout "@{5}" + +Jeśli chciałbyś pogłębić wiedze na ten temat przeczytaj sekcję ``Specifying Revisions`` w *git help rev-parse*. + +Byś może zechcesz zmienić okres karencji dla przeznaczonych na stracenie 'commits'. Na przyklad: + + $ git config gc.pruneexpire "30 days" + +znaczy, że skasowany 'commit' zostanie nieuchronnie utracony dopiero po 30 dniach od wykonania polecenia *git gc*. + +Jeśli chcałbyś zapobiec automatyycznemu wykonywaniu *git gc*: + + $ git config gc.auto 0 + +wtedy 'commits' będą tylko wtedy usuwane, gdy ręcznie wykonasz polecenie *git gc*. + +=== Budować na bazie Gita === + +W prawdziwym unixowym świecie sama konstrukcja Gita pozwala na wykorzystanie go, jako komponent niskiego poziomu, przez inne aplikacje, jak na przykład interfejsy graficzne i aplikacje internetowe, alternatywne narzędzia konsoli, narzędzia patchujące, narzędzia pomocne w importowaniu i konwertowaniu i tak dalej. Nawek same polecenia git są czasami malutkimi skryptami, jak krasnoludki na ramieniu olbrzyma. Przykładając trochę ręki możesz adoptować Git do twoich własnych potrzeb. + +Prostą sztuczką może być korzystanie z zintegrowanej w git funkcji aliasu, by skrócić najczęściej stosowane polecenia: + + $ git config --global alias.co checkout + $ git config --global --get-regexp alias # wyświetli aktualne aliasy + alias.co checkout + $ git co foo # to samo co 'git checkout foo' + +Czymś troszeczkę innym będzie zapis nazwy aktualnego 'branch' w prompcie lub jako nazwy okna. Polecenie: + + $ git symbolic-ref HEAD + +pokaże nazwę aktualnego 'branch'. W praktyce chciałbyś raczej usunąć "refs/heads/" i ignorować błędy: + + $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- + +Podkatakog +contrib+ jest wielkim znaleziskiem narzędzi zbudowanych dla git. Z czasem niektóre z nich mogą uzyskać status oficjalnych poleceń. W dystrybucji Debian i Ubuntu znajdziemy ten katalog pod +/usr/share/doc/git-core/contrib+. + +Ulubionym przedstawicielem jest +workdir/git-new-workdir+. Poprzez sprytne przelinkowania skrypt ten tworzy nowy katalog roboczy, który dzieli swoją historię wersji z orginalnym repozytorium: + + $ git-new-workdir istniejacy/repo nowy/katalog + +Ten nowy katalog i znajdujące się w nim pliki można sobie wyobrazić jako klon, z tą różnicą, że ze względu na wspólną niepodzieloną historie obje wersje pozostaną zsynchronizowane. Synchronizacja za pomocą 'merge', 'push', czy 'pull' nie będzie konieczna. + +=== Śmiałe wyczyny === + +Obecnie Git dość dobrze chroni użytkownika przed przypadkowym zniszczeniem danych. Ale, jeśli wiemy co robić, możemy obejść środki ochrony najczęściej stosowanych poleceń. + +*Checkout*: nie wersjonowane zmiany doprowadzą do niepowodzenia polecenia 'checkout'. Aby mimo tego zniszczyć zmiany i przywołać istniejący 'commit', możemy skorzystać z opcji 'force': + + $ git checkout -f HEAD^ + +Jeśli poleceniu 'checkout' podamy inną ścieżkę, środki ochrony nie znajdą zastosowania. Podana ścieżka zostanie bez pytania zastąpiona. Bądź ostrożny stosując 'checkout' w ten sposób. + +*reset*: reset odmówi pracy, jeśli znajdzie niewersjonowane zmiany. By zmusić go do tego, możesz użyć: + + $ git reset --hard 1b6d + +*Branch*: Skasowanie 'branches' też się nie powiedzie, jeśli mogłyby przez to zostać utracone zmiany. By wymusić skasowanie, podaj: + + $ git branch -D martwy_branch # zamiast -d + +Również nie uda się próba przesunięcia 'branch' poleceniem 'move', jeśliby miałoby to oznaczać utratę danych. By wymusić przesunięcie, podaj: + + $ git branch -M źródło cel # zamiast -m + +Inaczej niż w przypadku 'checkout' i 'reset', te oba polecenia przesuną zniszczenie danych. Zmiany te zostaną zapisane w podkatalogu .git i mogą znów zostać przywrócone, jeśli znajdziemy odpowiedni kluch SHA1 w `.git/logs` (zobacz "Łowcy głów" powyżej). Standardowo dane te pozostają jeszcze przez 2 tygodnie. + +*clean*: Różnorakie polecenia Git nie chcą się zadziałać, ponieważ podejżewają konflikty z niewersjonowanymi danymi. Jeśli jesteś pewny, że wszystkie niezwersjonowane pliki i katalogi są zbędne, skasujesz je bezlitośnie poleceniem: + + $ git clean -f -d + +Następnym razem te uciążliwe polecenia zaczną znów być posłuszne. + +=== Zapobiegaj złym 'commits' === + +Głupie błędy zaśmiecają moje repozytoria. Najbardziej fatalny jest brak plików z powodu zapomnianych *git add*. Mniejszymi usterkami mogą być spacje na końcu linii i nierozwiązane konflikty poleceń 'merge': mimo iż nie są groźne, życzyłbym sobie, by nigdy nie wystąpiły publicznie. + +Gdybym tylko zabezpieczył się wcześniej, stosując prosty _hook_, który alarmowałby mnie przy takich problemach. + + $ cd .git/hooks + $ cp pre-commit.sample pre-commit # Starsze wersje Gita wymagają jeszcze: chmod +x pre-commit + +I już 'commit' przerywa, jeśli odkryje niepotrzebne spacje na końcu linii albo nierozwiązane konflikty 'merge'. + +Na początku *pre-commit* tego 'hook' umieściłbym dla ochrony przed rozdrobnieniem: + + if git ls-files -o | grep '\.txt$'; then + echo FAIL! Untracked .txt files. + exit 1 + fi + +Wiele operacji Gita pozwala na używanie 'hooks'; zobacz też: *git help hooks*. We wcześniejszcm rozdziale "Git poprzez http" przytoczyliśmy przykład 'hook' dla *post-update*, który wykonywany jest zawsze, jeśli znacznik 'HEAD' zostaje przesunięty. Ten przykładowy skrypt 'post-update' aktualizuje dane, które potrzebne są do komunikacji poprzez 'Git-agnostic transports', jak na przykład HTTP. diff --git a/tmp/history.txt b/tmp/history.txt new file mode 100644 index 0000000..caf1aaa --- /dev/null +++ b/tmp/history.txt @@ -0,0 +1,197 @@ +== Lekcja historii == + +Jedną z charakterystycznych cech rozroszonej natury Git jest to, że jego kronika historii może być łatwo edytowana. Ale jeśli masz zamiar manipulować przeszłością, bądź ostrożny: zmieniaj tylko tą część historii, którą wyłącznie jedynie ty sam posiadasz. Tak samo jak Narody ciągle dyskutują, który jakie popełnił okrucieństwa, popadniesz w kłopoty przy synchronizacji, jeśli ktoś inny posiada klon z różniącą się historią i jeśli te odgałęzienia mają sie wymieniać. + +Niektórzy programiści zarzekają sie w kwestii nienaruszalności historii - ze wszystkimi jej błędami i niedociągnięciami. Inni uważają, ze odgałęzienia powinny dobrze się prezentować nim zostaną przedstawione publicznie. Git jest wyrozumiały dla obydwóch stron. Tak samo jak 'clone', 'branche' czy 'merge', możliwość zmian kroniki historii to tylko kolejna mocna strona Gita. Stosowanie lub nie, tej możliwości zależy wyłącznie od ciebie. + +=== Muszę się skorygować === + +Właśnie wykonałeś 'commit', ale chętnie chciałbyś podać inny opis? Wpisujesz: + + $ git commit --amend + +by zmienić ostatni opis. Zauważasz jednak, że zapomniałeś dodać jakiegoś pliku? Wykonaj *git add*, by go dodać a następnie poprzednią instrukcje. + +Chcesz wprowadzić jeszcze inne zmiany do ostatniego 'commit'? Wykonaj je i wpisz: + +$ git commit --amend -a + +=== ... i jeszcze coś === + +Załóżmy, że poprzedni problem będzie 10 razy gorszy. Po dłuższej sesji zrobiłeś całą masę 'commits'. Nie jesteś jednak szczęśliwy z takiego zorganizowania a niektóre z 'commits' mogłyby być inaczej sformułowane. Wpisujesz: + + $ git rebase -i HEAD~10 + +i ostatnie 10 'commits' pojawią się w preferowanym przez ciebie edytorze. Przykładowy wyciąg: + + pick 5c6eb73 Added repo.or.cz link + pick a311a64 Reordered analogies in "Work How You Want" + pick 100834f Added push target to Makefile + +Starcze 'commits' poprzedzają młodsze, inaczej niż w poleceniu `log` +Tutaj 5c6eb73 jest nasjstarszym 'commit', a 100834f najnowszym. By to zmienić: + +- Usuń 'commits' poprzez skasowanie lini. Podobnie jak polecenie 'revert', będzie to jednak wyglądało jakby wybrane 'commit' nigdy nie istniały. +- Przeorganizuj 'commits' przesuwając linie. +- Zamień `pick` na: + * `edit` by zaznaczyć 'commit' do 'amend'. + * `reword`, by zmienić opisy logu. + * `squash` by połączyć 'commit' z poprzednim ('merge'). + * `fixup` by połączyć 'commit' z poprzednim ('merge') i usunąć zapisy z logu. + +Na przykład chcemy zastąpić drugi `pick` na `squash`: + +Zapamietaj i zakończ. Jeśli zaznaczyłeś jakiś 'commit' poprzez 'edit', wpisz: + + $ git commit --amend + + pick 5c6eb73 Added repo.or.cz link + squash a311a64 Reordered analogies in "Work How You Want" + pick 100834f Added push target to Makefile + +After we save and quit, Git merges a311a64 into 5c6eb73. Thus *squash* merges +into the next commit up: think ``squash up''. + +Git then combines their log messages and presents them for editing. The +command *fixup* skips this step; the squashed log message is simply discarded. + +If you marked a commit with *edit*, Git returns you to the past, to the oldest +such commit. You can amend the old commit as described in the previous section, +and even create new commits that belong here. Once you're pleased with the +``retcon'', go forward in time by running: + + $ git rebase --continue + +Git replays commits until the next *edit*, or to the present if none remain. + +You can also abandon the rebase with: + + $ git rebase --abort + +A więc, stosuj polecenie 'commit' wcześnie i często: możesz później posprzątać za pomocą 'rebase'. + +=== Lokalne zmiany na koniec === + +Pracujesz nad aktywnym projektem. Z biegiem czasu nagromadziło się wiele 'commits' i wtedy chcesz zsynchronizować za pomocą 'merge' z oficjalną gałęzią. Ten cykl powtarza się kilka razy zanim jesteś gotowy na 'push' do centralnego drzewa. + +Teraz jednak historia w twoim lokalnym klonie jest chaotycznym pomieszaniem twoich zmian i zmian z oficjalnego drzewa. Chciałbyś raczej widzieć twoje zmiany uporządkowane chronologichnie w jednej sekcji i za oficjalnymi zmianami. + +To zadanie dla *git rebase*, jak opisano powyżej. W wielu przypadkach możesz skorzystać z przełącznika *--onto* by zapobiec interakcji. + +Przeczytaj też *git help rebase* dla zapoznania sie z obszernymi przykładami tej zadziwiającej funkcji. Możesz również podzielć 'commits'. Możesz nawet przeorganizować 'branches' w repozytorium. + +Bądź ostożny korzystając z 'rebase', to bardzo mocne polecenie. Zanim dokonasz skomplikowanych 'rebase', zrób backup za pomocą *git clone* + +=== Przepisanie historii === + +Czasami potrzebny ci rodzaj systemu kontroli porównywalnego do wyretuszowania osób z oficjalnego zdjęcia, by w stalinistowski sposób wymazać je z historii. Wyobraź sobie, że chcesz opublikować projekt, jednak zawiera on pewny plik, który z jakiegoś ważnego powodu musi pozostać utajniony. Być może zapisałem numer karty kredytowej w danej tekstowej i nieumyślnie dodałem do projektu? Skasowanie tej danej nie ma sensu, ponieważ poprzez starsze 'commits' można nadal ją przywołać. Musimy ten plik usunąć ze wszystkich 'commits': + + $ git filter-branch --tree-filter 'rm bardzo/tajny/plik' HEAD + +Sprawdź *git help filter-branch*, gdzie przykład ten został wytłumaczony i przytoczona została jeszcze szybsza metoda. Ogólnie poprzez *filter-branch* da się dokonać zmian w dużych zakresach historii poprzez tylko jedno polecenie. + +Po tej operacji katalog +.git/refs/original+ opisuje stan przed jej wykonaniem. Sprawdź czy 'filter-branch' zrobił to, co od niego oczekiwałeś, następnie skasuj ten katalog zanim wykonasz następne polecenia 'filter-branch'. + +Wreszcie zamień wszystkie klony twojego projektu na zaktualizowaną wersję, jeśli masz zamiar prowadzić z nimi wymianę. + +=== Tworzenie historii === + +[[makinghistory]] +Masz zamiar przenieść projekt do Gita? Jeśli twój projekt był dotychczas zarządzany jednym z bardziej znanch systemów, to istnieje duże prawdopodobieństwo, że ktoś napisał już odpowiedni skrypt, który umożliwi ci eksportowanie do Gita całej historii. + +W innym razie przyjrzyj się funkcji *git fast-import*, która wczytuje tekst w specjalnym formacie by następnie odtworzyć całą historię od początku. Często taki skrypt pisany jest pośpiesznie i służy do jednorazowego wykorzystania, aby tylko w jednym przebiegu udała się migracja projektu. + +Utwórz na przykład z następującej listy tymczasowy plik, na przykład jako: `/tmp/history`: ---------------------------------- +commit refs/heads/master +committer Alice Thu, 01 Jan 1970 00:00:00 +0000 +data < + +int main() { + printf("Hello, world!\n"); + return 0; +} +EOT + + +commit refs/heads/master +committer Bob Tue, 14 Mar 2000 01:59:26 -0800 +data < + +int main() { + write(1, "Hello, world!\n", 14); + return 0; +} +EOT + +---------------------------------- + +Następnie utwórz repozytorium git z tymczasowego pliku poprzez wpisanie: + + $ mkdir project; cd project; git init + $ git fast-import --date-format=rfc2822 < /tmp/history + +Aktualną wersję projektu możesz przywołać ('checkout') poprzez: + + $ git checkout master + +Polecenie *git fast-export* konwertuje każde repozytorium do formatu *git fast-import*, możesz przestudiować komunikaty tego polecenia, jeśli masz zamiar napisaś programy eksportujące a oprócz tego, by przekazywać repozytoria jako czytelne dla ludzi zwykłe pliki tekstowe. To polecenie potrafi przekazywać repozytoria za pomocą zwykłego pliku tekstowego. + +=== Gdzie wszystko się zepsuło? === + +Właśnie znalazłeś w swoim programie funkcję, która już nie chce działać, a jesteś pewna, że czyniła to jeszcze kilka miesięcy temu. Ach! Skąd wziął się ten błąd? A gdybym tylko lepiej przetestowała ją wcześniej, zanim weszła do wersji produkcyjnej. + +Na to jest już za późno. Jakby nie było, pod warunkiem, że często używałeś 'commit', Git może ci zdradzić gdzie szukać problemu. + + $ git bisect start + $ git bisect bad HEAD + $ git bisect good 1b6d + +Git przywoła stan, który leży dokładnie pośrodku. Przetestuj funkcję, a jeśli ciągle jeszcze nie działa: + + $ git bisect bad + +Jeśli nie, zamień "bad" na "good". Git przeniesie cię znowu do stanu dokładnie pomiędzy znanymi wersjami "good" i "bad", redukując w ten sposób możliwości. Po kilku iteracjach doprowadzą cię te poszukiwania do 'commit', który jest odpowiedzialny za kłopoty. Po skończeniu dochodzenia przejdź do orginalnego stanu: + + $ git bisect reset + +Zamiast sprawdzania zmian ręcznie, możesz zautomatyzowć poszukiwania za pomocą skryptu: + +$ git bisect run moj_skrypt + +Git korzysta tutaj z wartości zwróconej przez skrypt, by ocenić czy zmiana jest dobra ('good'), czy zła ('bad'): Skrypt powinien zwracać 0 dla 'good', 128, jeśli zmiana powinna być pominięta, i coś pomiędzy 1 - 127 dla 'bad'. Jeśli wartość zwrócona jest ujemna, program 'bisect' przerywa pracę. + +Możesz robić jeszcze dużo innych rzeczy: w pomocy znajdziesz opis w jaki sposób wizualizować działania 'bisect', sprawdzić czy powtórzyć log bisect, wyeliminować nieistotne zmiany dla zwiększenia prędkości poszukiwań. + +=== Kto ponosi odpowiedzialność? === + +Jak i wiele innych systemów kontroli wersji również i Git posiada polecenie 'blame': + + $ git blame bug.c + +które komentuje każdą linię podanego pliku, by pokazać kto ją ostatnio zmieniał i kiedy. W przeciwieństwie do wielu innych systemów, funkcja ta działa offline, czytając tylko z lokalnego dysku. + +=== Osobiste doświadczenia === + +W scentralizowanym systemie kontroli wersji praca nad kroniką historii jest skomplikowanym zadaniem i zarezerwowanym głównie dla administratorów. Polecenia 'clone', 'branche' czy 'merge' nie są możliwe bez podłączenia do sieci. Również takie podstawowe funkcje, jak przeszukanie historii czy 'commit' jakiejś zmiany. W niektórych systemach użytkownik potrzebuje działającej sieci nawet by zobaczyć dokonane przez siebie zmiany, albo by wogóle otworzyć plik do edycji. + +Scentralizowane systemy wykluczają pracę offline i wymagają drogiej infrastruktury sieciowej, w szczególności gdy wzrasta liczba programistów. Najgorsze jednak, iż z czasem wszystkie operacje stają się wolniejsze, z regóły do osiągnięcia punktu, gdzie użytkownicy unikają zaawansowanch poleceń, aż staną się one absolutnie konieczne. W ekstremalnych przypadkach dotyczy to również poleceń podstawowych. Jeśli użytkownicy są zmuszeni do wykonywania powolnych poleceń, produktywność spada, ponieważ ciągle przerywany zostaje tok pracy. + +Dowiedziałem się o tym fenomenie z pierwszej ręki. Git był pierwszym systemem kontroli wersji którego używałem. Szybko dorosłem do tej aplikacji i przyjąłem wiele funkcji za oczywiste. Wychodziłem też z założenia, że inne systemy są podobne: wybór systemu kontroli wersji nie powinien zbyt bardzo odbiegać od wyboru edytora tekstu, czy przeglądarki internetowej. + +Byłem zszokowany, gdy musiałem później korzystać ze scentralizowanego systemu. Niesolidne połączenie internetowe ma niezbyt duży wpływ na Gita, praca staje się jednak prawie nie możliwa, gdy wymagana jest niezawodność porównywalny z lokalnym dyskiem. Pozatym sam łapałem sie na tym, że unikałem pewnych poleceń i związanym z nimi czasem oczekiwania, w sumie wszystko to wpływało mocno na wypracowany przeze mnie system pracy. + +Gdy musiałem wykonywać powolne polecenia, z powodu ciągłego przerywanie toku myślenia, powodowałem nieporównywalne szkody dla całego przebiegu pracy. Podczas oczekiwania na zakończenie komunikacji pomiędzy serwerami dla przeczekania zaczynałem robiłć coś innego, na przykład czytałem maile albo pisałem dokumentację. Gdy wracałem do poprzedniego zajęcia, po zakończeniu komunikacji, dawno straciłem wątek i traciłem czas, by znów przypomnieć sobie co właściwie miałem zamiar zrobić. Ludzie nie potrafią dobrze dostosować się do częstej zmiany kontekstu. + +Był też taki ciekawy efekt http://pl.wikipedia.org/wiki/Tragedia_wsp%C3%B3lnego_pastwiska[tragedii-wspólnego-pastwiska]: przypominający przeciążenia w sieci - pojedyńcze indywidua pochłaniają więcej pojemności sieci niż to konieczne, by uchronić się przed mogącymi ewentualnie wystąpić w przyszłości niedoborami. Suma tych starań pogarsza tylko przeciążenia, co motywuje jednostki do zużywania jeszcze większych zasobów, by ochronić się przed jeszcze dłuższymi czasami oczekiwania. diff --git a/tmp/intro.txt b/tmp/intro.txt new file mode 100644 index 0000000..2dab7f2 --- /dev/null +++ b/tmp/intro.txt @@ -0,0 +1,57 @@ +== Wprowadzenie == + +By wprowadzić w zagadnienie kontroli wersji, posłużę się pewną analogią. Dla bardziej rozsądnego wyjaśnienia przeczytajcie http://pl.wikipedia.org/wiki/System_kontroli_wersji[Artykół Wikipedii na temat systemu kontroli wersji]. + +=== Praca jest zabawą === + +Gram w gry komputerowe chyba już przez całe moje życie. W przeciwieństwie do tego, systemy kontroli wersji zacząłem używać dopiero jako dorosły. Przypuszczam, że nie jestem tu odosobniony, a porównanie to pomoże mi w prosty sposób wytłumaczyć zrozumiale jej koncept. + +Wyobraź sobie pracę nad twoim kodem albo dokumentami jak granie na komputerze. Jeśli dobrze ci poszło, chciałabyś zabezpieczyć swoje osiągnięcia. W tym celu klikasz na 'zapisz' w wybranym edytorze. + +Jednak, przepiszesz przez to poprzednio zapamiętaną wersję. To jak w grach starej szkoły, które posiadały pamięć na zapisanie tylko jednego stanu: oczywiście, mogłeś zapamiętać, ale już nigdy nie mogłeś powrócic do starszego stanu. To była hańba, bo być może poprzednio zabezpieczony stan był w jakimś bardzo interesującym miejscu gry, do którego chętnie chciałbyś jeszcze wrócić. Albo jeszcze gorzej, twój zabezpieczony stan gry utknął w miejsu niemożliwym do rozwiązania i musisz zaczynać wszystko od początku. + +=== Kontrola wersji === + +Podczas edytowania, by uchronić starą wersję, możesz poprzez wybranie 'zapisz jako ...' zapisać twój dokument pod inną nazwą lub zapamiętać w innym miejscu. Poza tym możesz go jeszcze spakować, by zaoszczędzić miejsce na dysku. Jest to prymitywna i pracochłonna forma kontroli wersji. Gry komputerowe robią tak już od długiego czasu, wiele z nich posiada automatycznie utworzone punkty opatrzone sygnaturą czasu. + +Skomplikujmy teraz trochę cały ten problem. Powiedzmy, że posiadasz całą masę plików, które w jakiś sposób są ze sobą powiązane, na przykład kod źródłowy jakiegoś projektu lub pliki strony internetowej. Jeśli chcesz otrzymać starszą wersję musisz archiwizować cały katalog. Archiwizowanie w ten sposób wielu wersji jest pracochłonne i szybko może stać się kosztowne, zabierając niepotrzebnie miejsce na dysku. + +Niektóre gry komputerowe składały sie rzeczywiście z jednego katalogu pełnego plików. Gry ukrywały szczegóły przed graczem i prezentowały wygodny interfejs, do zarządzania różnymi wersjami katalogu. + +Systemy kontroli wersji nie różnią się tutaj zbytnio. Wszystkie posiadają wygodne interfejsy, aby zarządzać katalogami pełnymi plików. Możesz archiwizować stan katalogu tak często jak często chcesz i później możesz do każdego z tych punktów powrócić. W przeciwieństwie jednak do gier, są one z regóły wszystkie zoptymalizowane pod kątem oszczędności pamięci. W więlszości przypadków tylko niewiele danych ulega zmianie pomiędzy dwoma wersjami, a same zmiany nie są zbyt obszerne. Oszczędność miejsca na dysku polega głównie na zapamiętywaniu jedynie różnic, a nie kopii całego katalogu. + +=== Rozproszona kontrola === + +Wyobraź sobie teraz bardzo skomplikowaną grę komputerową. Tak trudną, że wielu doświadczonych graczy na całym świecie postanawia o współnych siłach przejść grę, wymieniając się w tym celu swoimi zapamiętanymi wynikami. 'Speedruns' mogą posłużyć jako przykład z prawdziwego życia: gracze, którzy wyspecjalizowali się w różnych poziomach gry współpracują ze sobą dla uzyskania fascynujących wyników. + +W jaki sposób skonstruowałbyś taki system, który w prosty sposób jest w stanie otrzymywać archiwa od innych? Natomiast własne innym udostępni? + +Kiedyś każdy projekt korzystał z własnego scentralizowanego systemu kontroli wersji. Jeden serwer zapamiętywał wszystkie gry, nikt inny. Każdy gracz posiadał jedynie kilka zapamiętanych na swoim komputerze gier. Jeśli jakiś gracz chciał popchać grę trochę do przodu, musiał najpierw zładować ze serwera aktualny stan, trochę pograć, zapisać własny stan, a następnie załadować na serwer, by mógł go wykorzystać ktoś inny. + +A gdy jakiś gracz z jakiegoś powodu chce otrzymać jakiś starszy stan? Może aktualnie zapamiętany stan gry nie jest do przejścia, bo ktoś na trzecim poziomie zapomniał zabrać jakiś objekt, i teraz próbują znaleźć stan startując od którego gra staje się znowu możliwa do przejścia. Albo chcecie porównać dwa stany, by sprawdzić ile jakiś gracz przyczynił się. + +Jest wiele powodów, dla których można chcieć zobaczyć straszy stan, wynik jednak jest zawsze taki sam. Za każdym razem trzeba ściągnąć wszystkie dane z serwera. Czym więcej gier zostało zapamiętanych, tym więcej wymaga to komunikacji. + +Nowa generacja systemów kontroli wersji, do których należy również git, nazywana jest systemami rozproszonymi i mogą być rozumiane jako uogólnienie systemów scentralizowanych. Jeśli gracze ładują teraz z serwera, otrzymują każdy zapisany stan, nie tylko ostatnio zapisany Wygląda to jak klonowanie serwera. + +Pierwszy klon może być drogi, przede wszystkim, jeśli posiada długą historię, ale na dłuższy okres opłaci się. Jedną z bezpośrednich zalet jest to, że gdykolwiek potrzebny będzie jakiś starszy stan, komunikacja z głównym serwerem będzie zbędna. + +=== Głupi przesąd === + +Szeroko rozpowszechnianym nieporozumieniem jest opinia, że rozproszony system nie nadaje się dla projektów wymagających oficjalnego centralnego repozytorium. Nic nie jest bardziej oddalone od rzeczywistości. Fotografując kogoś nie kradziemy jego duszy. Tym samym klonowanie centralnego repozytorium nie umniejsza jego znaczenia. + +Jednym z pierszych pozytywnych zbliżeń, jest to, iż wszystko co potrafi scentralizowany system kontroli wersji, dobrze dopracowany system rozproszony potrafi lepiej. Zasoby sieciowe są po prostu droższe niż zasoby lokalne. Również, gdy w późniejszym czasie dostrzeżemy wady systemów rozproszonych, można to przyjąć jako ogólną zasade mniej podatną na złe porównania. + +Mały projekt wykorzysta prawdopodobnie tylko ułamek możliwości systemu. Ale, by z tego powodu korzystać z prostego systemu, nie posiadającego możliwości rozszerzenia, to tak jak stosowanie rzymskich cyfr do przeprowadzania obliczeń na małych liczbach. + +Poza tym może sie zdarzyć, że twój projekt daleko przerośnie początkowe oczekiwania. Używając git od samego początku, to jak noszenie ze sobą scyzoryka szwajcarskiego, nawet gdy najczęściej posłuży do otwierania butelek. Być może pewnego dnia będziesz pilnie potrzebawała użyć śrubokrętu, ucieszysz się, że masz przy sobie coś więcej niż tylko zwykły otwieracz do butelek. + +=== Konflikty z 'merge' === + +Do przedstawienia tego punktu wykorzystanie analogii do gier komputerowych nie jest odpowiednia. Zamiast tego wyobraźmy sobie znowu, że edytujemy jakiś dokument. + +Wyobraź sobie, Alicja dodaje linijkę na początku dokunentu, natomiast Bob na jego końcu. Obydwoje ładują swoje zmiany na serwer. Większość systemów wybierze automatycznie rozsądną drogę: zaakceptuje obie zmiany i połączy je ze sobą, tym samym obje poprawki wpłyną do dokumentu. + +Wyobraź sobie jednak, że Alicja i Bob dokonują zmian w tej samej lini. W tym wypadku dalsze praca nie będzie możliwa bez ludzkiego udziału. Druga z osób, próbująca zładować dokument na serwer, zostanie poinformowana o wystąpieniu konfliktu 'merge' i musi zdecydować, która ze zmian zostanie przyjęta lub ponownie zrewidować całą linijkę. + +Mimo to mogą wystąpić dużo bardziej skomplikowane sytuacje. Systemy kontroli wersji potrafią poradzić sobie z prostymi przypadkami a te trudniejsze pozostawiają ludziom. Zazwyczaj ich zachowanie daje się ustawić. diff --git a/tmp/multiplayer.txt b/tmp/multiplayer.txt new file mode 100644 index 0000000..6ce5563 --- /dev/null +++ b/tmp/multiplayer.txt @@ -0,0 +1,163 @@ +== Multiplayer Git == + +Na początku zastosowałem git przy prywatnym projekcie, gdzie byłem jedynym developerem. Z poleceń w związku z rozproszoną naturą git, potrzebowałem jedynie poleceń *pull* i *clone*, dzięki czemu mogłem trzymać ten sam projekt w kilku miejscach. + +Później chciałem opublikować mój kod za pomocą git i dołączyć zmiany kolegów Musiałem nauczyć się zarządzać projektami, nad którymi zaangażowani byli programiści z całego świata. Na szczęście jest to silną stroną git i chyba jego racją bytu. + +=== Kim jestem? === + +Każdy 'commit' otrzymuje nazwę i adres email autora, które zostaną pokazane w *git log*. Standardowo git korzysta z ustawień systemowych do wypełnienia tych pól. Aby wprowadzić te dane bezpośrednio, podaj: + +$ git config --global user.name "Jan Kowalski" $ git config --global user.email jan.kowalski@example.com + +Jeśli opóścisz przełącznik '--global' zmiany zostaną zastosowane wyłącznie do aktualnego repozytorium. + +=== Git przez SSH, HTTP === + +Załóżmy, posiadasz dostęp SSH do serwera stron internetowych, gdzie jednak git nie został zainstalowany. Nawet, jeśli jest to mniej efektywne jak własny protokół git, git potrafi komunikować się przez HTTP. + +Zładuj git, skompiluj i zainstaluj pod własnym kontem oraz utwórz repozytorium w twoim katalogu strony internetowej. + +$ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update + +Przy starszych wersjach git samo polecenie 'cp' nie będzie funkcjonować, wtedy musisz jeszcze: + +chmod a+x hooks/post-update + +Od teraz możesz publikować aktualizacje z każdego klonu poprzez SSH. + +$ git push web.server:/sciezka/do/proj.git master + +i każdy może teraz sklonować twój projekt przez: + +$ git clone http://web.server/proj.git + +=== Git ponad wszystko === + +Chciałbyś synchronizować repozytoria bez pomocy serwera czy nawet bez użycia sieci komputerowej? Musisz improwizować w nagłym wypadku? Widzieliśmym, że poleceniami <>. W ten sposób możemy transportować tego typu pliki za pomocą dowolnego medium, jednak bardziej wydajnym narzędziem jest *git bundle*. + +Nadawca tworzy 'bundle': + +$ git bundle create plik HEAD + +i transportuje 'bundle' +plik+ do innych zaangażowanych: przez email, stik-usb, *xxd* hexdump i skaner OCR, kod morse przez telefon, znaki dymne itd. Odbiorca wyciąga 'commits' z 'bundle' poprzez podanie: + + +$ git pull plik + +Odbiorca może to zrobić z pustym repozytorium. Mimo swojej wielkości +plik+ zawiera kompletny orginał repozytorium. + +W dużych projektach unikniesz śmieci danych, jeśli tylko zrobisz 'bundle' zmian brakujących w innych repozytoriach. Na przykład załóżmy, że 'commit' ``1b6d...'' jest najaktualniejszym, które posiadają obie partie: + + +$ git bundle create plik HEAD ^1b6d + +Jeśli robi się to regularnie, łatwo można zapomnieć, który 'commit' został wysłany ostatnio. Strony pomocy zalecają stosowanie tagów, by rozwiązać ten problem. To znaczy, po wysłaniu 'bundle', podaj: + +$ git tag -f ostatnibundle HEAD + +a nowy 'bundle' tworzymy następnie poprzez: + +$ git bundle create nowybundle HEAD ^ostatnibundle + +=== Patches: globalny środek płatniczy === + +'Patches' to jawne zobrazowanie twoich zmian, które mogą być jednocześnie rozumiane przez komputer i człowieka. Dodaje im to uniwersalnej mocy przyciągania. Możesz wysłać patch prowadzącym projekt, niezależnie od tego, jakiego używają systemu kontroli wersji. Doputy twoi współpracownicy potrafią czytać swoje maile, mogą widzieć również twoje zmiany. Również i z twojej strony wszystko, czego ci potrzeba to fukcjonujące konto mailowe: nie istnieje konieczność zakładania repozytorium online. + +Przypomnij sobie pierwszy rozdział: + +$ git diff 1b6d > moj.patch + +produkuje 'patch', który można dołączyć do maila dla dalszej dyskusji. W repozytorium git natomiast podajesz: + +$ git apply < moj.patch + +By zastosować patch. + +W bardziej oficjalnym środowisku, jeśli nawiska autorów i ich sygnatury powinny również być notowane, tworz 'patch' od pewnego punktu, po wpisaniu: + +git format-patch 1b6d + +Uzyskane w ten sposób dane mogą przekazane być do *git-send-mail* albo odręcznie wysłane. Możesz podać grupę 'commits' + +$ git format-patch 1b6d..HEAD^^ + +Po stronie odbiorcy zapamiętaj email jako daną i podaj: + +$ git am < email.txt + +Patch zostanie wprowadzony i utworzy commit, włącznie z informacjami jak naprzykład o autorze. + +Jeśli stosujesz webmail musisz ewentualnie kliknąć, by pokazać treść niesformatowaną, zanim zapamiętasz patch do pliku. + +Występują minimalne różnice między aplikacjami emailowymi bazującymi na mbox, ale jeśli korzystasz z takiej, należysz do grupy ludzi która za pewne umią się z nimi obchodzić bez czytania instrukcji! + +=== Przepraszamy, przeprowadziliśmy się === + +Po sklonowaniu repozytorium, polecenia *git push* albo *git pull* będą automatycznie wskazywały na orginalne URL. Jak Git to robi? Tajemnica leży w konfiguracji, która utworzona zostaje podczas klonowania. Zaryzykujmy spojrzenie: + +$ git config --list + +Opcja +remote.origin.url+ kontroluje źródłowe URL; ``origin'' to alias, nadany źródłowemu repozytorium. Tak jak i przy konwencji z ``master'' 'Branch' , możemy ten alias zmienić albo skasować, zwykle jednak nie ma powodów by to robić. + +Jeśli orginalne repozytorium zostanie przesunięte, możemy zaktualizować link poprzez: + +git config remote.origin.url git://nowy_link/proj.git + +Opcja +branch.master.merge+ definuje standardowy Remote-'Branch' dla *git pull*. Podczas początkowego klonowania, zostanie ustawiony na aktualny branch źródłowego repozytorium, że nawet i potym jak 'HEAD' źródłowego repozytorium przejdzie do innego branch, późniejszy 'pull' pozostanie wierny orginalnemu branch. + +Ta opcja jest ważna jedynie dla repozytorium, z którego dokonało się pierwsze klonowanie, co zapisane jest w opcji +branch.master.remote+. Przy 'pull' z innego repozytorium musimy podać z którego branch chcemy korzystać. + +$ git pull git://example.com/inny.git master + +To wyjaśnia dlaczego nasze poprzednie przykłady z 'push' i 'pull' nie posiadały argumentów. + +=== Oddalone 'Branches' === + +Jeśli klonujesz repozytorium, klonujesz równierz wszystkie jego 'branches' Może jeszcze tego nie zauważyłeś, ponieważ Git je ukrywa: musisz się o nie specjalnie pytać: To zapobiega temu, że branches z oddalonego repozytorium nie przeszkadza twoim lokalnym branches i czyni to Git łatwiejszym dla początkujących. + +Oddalone 'branches' możesz pokazać poprzez: + +$ git branch -r + +Powinieneś zobaczyć coś jak: + +origin/HEAD origin/master origin/experimental + +Lista ta ukazje BRANCHES i HEAD odległego repozytorium, które mogą być również stosowane w zwykłych poleceniach Git. Przyjmijmy, na przykład, że wykonałeś wiele COMMITTS i chciałbyś uzyskać porównanie do ostatnio ściągniętej wersji. Możesz przeszukać logi za odpowiednim kluczem SHA1, ale dużo prościej jest podać: + +$ git diff origin/HEAD + +Możesz też sprawdzić co działo się w 'Branch' ``experimental'' + +$ git log origin/experimental + +=== Więcej serwerów === + +Przyjmijmy, dwóch innych programistów pracuje nad twoim projektem i chcielibyśmy mieć ich na oku. Możemy obserwować więcej niż jedno reposytorium jednocześnie: + +$ git remote add inny git://example.com/jakies_repo.git $ git pull inny jakis_branch + +Teraz przyłączyliśmy jeden branch z dwóch repozytorii i uzyskaliśmy łatwy dostęp do wszystkich branch z wszystkich repozytorii. + +$ git diff origin/experimental^ inny/jakis_branch~5 + +Co jednak zrobić, gdy chcemy porównać zmiany w nich bez wpływu na naszą pracę? Innymi słowami, chcemy zbadać ich branches bez importowania ich zmian do naszego katalogu roboczego. Zamiast 'pull' skorzystaj z: + +$ git fetch # Fetch z origin, jako standard. $ git fetch inne # Fetch od drugiego programisty. + +Polecenie to załaduje jedynie historię Mimo, że nasz katalog pozostał bez zmian, możemy teraz referować z każdego repozytorium poprzez polecenia Gita, ponieważ posiadamy lokalną kopię. + +Przypomnij sobie, że 'pull' za kulisami to to samo co 'fetch' z następującym za nim *merge*. W normalnym wypadku wykonalibyśmy *pull*, bo chcielibyśmy przywołać również ostatnie 'commmits'. Ta przywołana sytuacja jest wyjątkiem wartym wspomnienia. + +Sprawdź *git help remote* by zobaczyć, jak usuwa się repozytoria, ignoruje pewne branches i więcej- + +=== Moje ustawienia === + +W moich projektach preferuję, gdy pomagający mi programiści przygotują własne repozytoria, z których mogę wykonać 'pull'. Większość hosterów Gita pozwala na utworzenie jednym kliknięciem twojego własnego forka innego projektu. + +Gdy przywołałem moją gałęź korzystam z poleceń Gita dla nawigacji i kontroli zmian, które najłepiej gdy są dobrze zorganizowane i udokumentowane. Wykonuję 'merge' moich własnych zmian i przeprowadzam ewentualnie dalsze zmiany. Gdy już jestem zadowolony, 'push' do zentralnego repozytorium.- + +Mimo, iż dość rzadko otrzymuję posty, jestem zdania, że ta metoda się opłaca. Zobacz http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[Post na Blogu Linusa Torvalds (po angielsku)]. + +Pozostając w świecie Gita jest wygodniejsze niż otrzymywanie patchów, ponieważ zaoszczędza mi to konwertowanie ich do 'commits' Gita. Pozatym Git martwi się o szczegóły, jak nazwa autora i adres maila, tak samo jak i o datę i godzinę oraz motywuje autora do opisywania swoich zmian. \ No newline at end of file diff --git a/pl/basic.txt b/tmp/orig/basic.txt similarity index 100% copy from pl/basic.txt copy to tmp/orig/basic.txt diff --git a/pl/branch.txt b/tmp/orig/branch.txt similarity index 100% copy from pl/branch.txt copy to tmp/orig/branch.txt diff --git a/pl/clone.txt b/tmp/orig/clone.txt similarity index 100% copy from pl/clone.txt copy to tmp/orig/clone.txt diff --git a/pl/drawbacks.txt b/tmp/orig/drawbacks.txt similarity index 100% copy from pl/drawbacks.txt copy to tmp/orig/drawbacks.txt diff --git a/pl/grandmaster.txt b/tmp/orig/grandmaster.txt similarity index 100% copy from pl/grandmaster.txt copy to tmp/orig/grandmaster.txt diff --git a/pl/history.txt b/tmp/orig/history.txt similarity index 100% copy from pl/history.txt copy to tmp/orig/history.txt diff --git a/pl/intro.txt b/tmp/orig/intro.txt similarity index 100% copy from pl/intro.txt copy to tmp/orig/intro.txt diff --git a/pl/multiplayer.txt b/tmp/orig/multiplayer.txt similarity index 100% copy from pl/multiplayer.txt copy to tmp/orig/multiplayer.txt diff --git a/pl/preface.txt b/tmp/orig/preface.txt similarity index 100% copy from pl/preface.txt copy to tmp/orig/preface.txt diff --git a/pl/secrets.txt b/tmp/orig/secrets.txt similarity index 100% copy from pl/secrets.txt copy to tmp/orig/secrets.txt diff --git a/pl/translate.txt b/tmp/orig/translate.txt similarity index 100% copy from pl/translate.txt copy to tmp/orig/translate.txt diff --git a/tmp/preface.txt b/tmp/preface.txt new file mode 100644 index 0000000..8cacc40 --- /dev/null +++ b/tmp/preface.txt @@ -0,0 +1,45 @@ += Git Magic = Ben Lynn Sierpień 2007 + +== Przedmowa == + +http://git.or.cz/[Git] to rodzaj scyzoryka szwajcarskiego dla kontroli wersji. Niezawodne, wielostronne narzędzie do kontroli wersji, jego niezwykła elastyczność sprawia trudności w poznaniu, nie wspominając o samym użyciu. + +Jak stwierdźił Arthur C. Clarke, każda wystarczająco postępowa technologia jest porównywalna z magią. Jest to wspaniałe podejście, by zacząć pracę z Git: Amatorzy mogą zognorować swoje wewnętrzene mechanizmy i ujrzeć Git jako rzecz, która urzeka przyjaciół swoimi niezwykłymi możliwościami, a przeciwników doprowadza do białej gorączki. + +Zamiast wchodzić głęboko w szczegóły, oferujemy proste instrukcje do odpowiednich funkcji. Podczas regularnego korzystania sam dojdziesz do tego, jak te sztuczki funkcjpnują i jak możesz dopasować podane instrukcje na swoje własne potrzeby. + +.Tłumaczenia + +- link:/\~blynn/gitmagic/intl/zh_cn/[uproszczony chiński]: od JunJie, Meng i JiangWei. Do link:/~blynn/gitmagic/intl/zh_tw/[tradycyjny chiński] skonwertowany przez +cconv -f UTF8-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/fr/[francuski]: od Alexandre Garel, Paul Gaborit, i Nicolas Deram. Również na http://tutoriels.itaapy.com/[itaapy]. - link:/~blynn/gitmagic/intl/de/[Deutsch]: od Benjamin Bellee i Armin Stebich; Również na http://gitmagic.lordofbikes.de/[Armin's Website]. - http://www.slideshare.net/slide_user/magia-git[portugalski]: od Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[Wersja ODT]]. - link:/~blynn/gitmagic/intl/ru/[rosyjski]: od Tikhon Tarnavsky, Mikhail Dymskov, i innych. - link:/~blynn/gitmagic/intl/es/[hiszpański]: od Rodrigo Toledo i Ariset Llerena Tapia. - link:/~blynn/gitmagic/intl/vi/[wietnamski]: od Trần Ngọc Quân; Również na http://vnwildman.users.sourceforge.net/gitmagic.html[jego Stronie]. + +.Inne wydania + +- link:book.html[pojedyńcze strony]: czysty HTML, bez CSS. - link:book.pdf[PDF Datei]: przyjazne do druku. - http://packages.debian.org/gitmagic[Pakiet Debiana], http:://packages.ubuntu.com/gitmagic[Pakiet Ubuntu]: Dla szybkiej lokalnej kopii tej strony. Praktyczne, http://csdcf.stanford.edu/status/[gdyby ten serwer był offline]. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Drukowana książka [Amazon.com]]: 64 Strony, 15.24cm x 22.86cm, czarno-biała. Praktyczna, gdyby zabrakło prądu. + +=== Dziękuję! === + +Jestem mile zaskoczony, że tak dużo ludzi pracowało nad przetłumaczeniem tych stron. bardzo doceniam to, że dzięki staraniom tylu wyżej wymienionych osób mam możliwość osiągnąć większy zakres czytelników. + +Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, i Tyler Breisacher przyczynilli się do poprawek i korektur. + +François Marier jest mentorem pakietu Debiana, który uprzednio utworzony został przez Daniela Baumann. + +Chcałbym podziękować również wszystkim innym za ich pomoc i dobre słowo. Chciałem was tu wszystkich wyszczegąlnić, mogłoby to jednak wzbudzić oczekiwania w szerokim zakresie. + +Gdybym o tobie przypadkowo zapomniał, daj mi znać albo przyślij mi po prostu patch. + +.Darmowe repozytoria Git + +- http://repo.or.cz/[http://repo.or.cz/] hostuje wolne projekty> Pierwsza powstała strona hostingowa dla Git. Założona i uprawiana przez jednego z pierwszych deweloperów Git. - http://gitorious.org/[http://gitorious.org/] to następna strona hostingowa Gita, preferująca Projekty Open-Source. - http://github.com/[http://github.com/] hostuje projekty Open-Source darmowo a projekty zamknięte za opłatą. + +Duże dzięki dla wszystkich tych stron za hosting tego poradnika. + +=== Lizencja === + +Ten poradnik publikowany jest na bazie licencji http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License Version 3]. Oczywiście, tekst źródłowy znajduje się w repozytorium Git i może zostać powielone przez: + +$ git clone git://repo.or.cz/gitmagic.git # Utworzy katalog "gitmagic". + +albo z lustrzanego serwera: + +$ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git \ No newline at end of file diff --git a/tmp/secrets.txt b/tmp/secrets.txt new file mode 100644 index 0000000..79dec33 --- /dev/null +++ b/tmp/secrets.txt @@ -0,0 +1,134 @@ +== Uchylenie tajemnicy == + +Rzućmy spojrzenie pod maskę silnika i wytłumaczymy w jaki sposób Git realizuje swoje cuda. Nie będę wchodził w szczegóły. Dla pogłębienia tematu odsyłam na http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[anielskojęzychny podręcznik użytkownika]. + +=== Niewidzialność === + +Jak to możliwe, że Git jest taki niepostrzeżony? Zapominając na chwilę o sporadycznych 'commits' i 'merges', możesz pracować w sposób, jakby kontrola wersji wogóle nie istniała. To znaczy - do czasu aż będzie ci potrzebna. A oto chodzi, byś był zadoowolony z tego, że Git cały czas czuwa nad twoją pracą + +Inne systemy kontroli wersji ciągle zmuszają cię do ciągłego borykania się z zagadnieniem samej kontroli i związanej z tym biurokracji. Pliki mogą być zapezpieczone przed zapisem, aż do momentu gdy uda ci się poinformować centralny serwer o tym, że chciałabyś nad nimi popracować. Przy wzroście liczby użytkowników nawet najprostsze polecenia stają się wolne jak ślimak. Gdy tylko zniknie sieć lub centralny serwer praca staje. + +W przeciwieństwie do tego, Git posiada kronikę całej swojej historii w podkatalogu .git twojego katalogu roboczego. Jest to twoja własna kopie całej historii, z którą mogłabyś pracować offline, aż do momentu gdy zechcesz wymienić dane z innymi. Posiadasz absolutną kontrolę nad losem twoich danych, ponieważ Git potrafi dla ciebie w każdej chwili odtworzyć zapamiętany poprzednio stan z właśnie podkatalogu .git. + +=== Integralność === + +Z kryptografią przez większość ludzi łączona jest poufność informacji, jednak równie ważnym jej celem jest zabezpieczenie danych. Właściwe zastosowanie kraptograficznych funkcji hashujących (funkcji skrótu) może uchronić przed nieumyślnym lub celowym zniszczeniem danych. + +Klucz hashujący SHA1 mogłabyś wyobrazić sobie jako składający się ze 160 bitów numer identyfikacyjny jednoznacznie opisujący dowolny łańcuch znaków, i który spotkasz w sowim życiu jeden jedyny raz. Nawet i więcej niż to: wszystkie łańcuchy znaków, jakie ludzkość przez wiele generacji stworzyła. + +sam kluch SHA1 też jest łańcuchem znaków w formie Bajtów. Możemy generować klucze SHA1 z łańcuchów samych zawierających klucze SHA1. Ta prosta obserwacja okazała się niesamowicie pożyteczna: jeśli cię to zainteresowało poszukaj informacji na temat 'hash chains'. Zobaczymy później w jaki sposób wykorzystje je Git dla zapewnienia produktywności i integralności danych. + +Krótko mówiąc, Git przechowuje twoje dane w podkatalogu `.git/objects`, gdzie zamiast nazw plików znajdziesz numery identyfikacyjne. Poprzez wykorzystanie tych numerów identyfikacyjnych jako nazwy plików razem z kilkoma innymi trikami związanymi z plikami blokującymi i znacznikami czasu, Git zamienia twój prosty system plików na produktywną i solidną bazę danych. + +=== Inteligencja === + +Skąd Git wie o tym, że zmieniłaś nazwę jakiegoś pliku, jeśli nigdy go o tym wyraźnie nie poinformowałaś? Oczywiście, być może użyłaś polecenia *git mv*, jest to jednak to samo jakbyś użyła *git rm*, a następnie *git add*. + +Git poszukuje heurystycznie zmian nazw w następującymch po sobie wersjach kopii. Dodatkowo potrafi czasami nawet znaleźć całe bloki z kodem przenoszonym tam i spowrotem między plikami! Mimo iż wykonuje kawał dobrej roboty, a ta właściwość staje się coraz lepsza, nie potrafi niestety jeszcze poradzić sobie z wszystkimi możliwymi przypadkami. Jeśli to u ciebie nie działa, spróbuj poszukać opcji rozszerzonego rozpoznawania kopii, aktualizacja samegi Git, też może pomóc. + +=== Indeksowanie === + +Dla każdego kontrolowanego pliku, Git zapamiętuje informacje o jego wielkości, czasie utworzenia i czasie ostatniej edycji w pliku znanym nam jako index. By ustalić, czy nastąpiła jakaś zmiana, Git porównuje stan aktualny ze stanem zapamiętanym w indeksie. Jeśli dane te nie różnią się, Git może pominąć czytanie zawartości pliku. + +Ponieważ sprawdzenie statusu pliku trwa dużo krócej niż jego całkowite wczytanie, to jeśli dokonałaś zmian tylko na kilku plikach Git zaktualizuje swój stan w mgnieniu oka. + +Stwierdziliśmy już wcześniej, że indeks jest przechowalnią (ang. staging area). Jak to możliwe, że stos informacji o statusie danych może być przechowywalnią? Ponieważ polecenie 'add' transportuje pliki do bazy danych Git i aktualizuje informacje o ich statusie, podczas gdy polecenie 'commit' (bez opcji) tworzy commit tylko wyłącznie na podstawie informacji o statusie plików, ponieważ pliki te już sie w tej bazie znajdują. + +=== Korzenie Git === + +Ten http://lkml.org/lkml/2005/4/6/121['Linux Kernel Mailing List' post] opisuje cały łańcuch zdarzeń, które inicjowały powstanie Git. Cały post jest archeologicznie fascynującą stroną dla historyków zajmujących sie Gitem. + +=== Obiektowa baza danych === + +Każda wersja twoich danych jest przechowywana w objektowej bazie danych, która znajduje sie w podkatalogu `.git/objects`. Inne miejsca w `.git/` posiadają mniej ważne dane, jak indeks, nazwy gałęzi ('branch'), tagi, logi,konfigurację, aktualną pozycję HEAD i tak dalej. Obiektowa baza danych jest prosta, mimo to jesnak elegancka i jest źródłem siły Gita. + +Każdy plik w `.git/objects` jest 'objektem'. Istnieją trzy rodzaje objektów, które nas interesują: 'blob', 'tree'-, i 'commit'. + +=== Bloby === + +Na początek magiczna sztuczka Wymyśl jakąś nazwę pliku, jakąkolwiek. W pustym katalogu: + + +$ echo sweet > TWOJA_NAZWA $ git init $ git add . $ find .git/objects -type f $ find .git/objects -type f + +Zobaczysz coś takiego: +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. + +Skąd mogłem to wiedzieć, mimo iż nie znałem nazwy pliku? Poniewaś wartość klucza SHA1 dla: + +"blob" SP "6" NUL "sweet" LF + +wynosi właśnie: aa823728ea7d592acc69b36875a482cdf3fd5c8d. Przyczym SP to spacja, NUL - to bajt zerowy, a LF to znak nowej linii ('newline'). Możesz to skontrolować wpisując: + +$ printf "blob 6\000sweet\n" | sha1sum + +Git pracuje asocjacyjnie (skojarzeniowo): dane nie są zapamiętywane na podstawie ich nazwy, tylko wartości ich własnego klucza SHA1 w pliku, który określamy mianem objektu 'blob'. Klucz SHA1 możemy sobie wyobrazić jako niepowtarzalny numer identyfikacyjny zawartości pliku, co oznacza, że pliki adresowane są na podstawie ich zawartości. Początkowe `blob 6`, to jedynie adnotacja, która określa jedynie rodzaj objektu i jego wieklość w bajtach, pozwala to na uproszczenie zarządzania wewnętrznego. + +Przez to właśnie mogłem 'przepowiedzieć' wynik. Nazwa pliku nie ma znaczenia, jedynie jego zawartość służy do utworzenia objektu 'blob'. + +Pytasz się, a co w przypadku identycznych plików? Spróbuj dodać kopie twojej danej pod jakąkolwiek nazwą. Zawartość +.git/objects+ nie zmieni się, niezależnie ile kopii dodałaś. Git zapamięta zawartość pliku wyłącznie jeden raz. + +Na marginesie, dane w +.git/objects+ są spakowane poprzez zlib, nie powinieneś otwierać ich bezpośrednio. Przefiltruj je najpierw przez http://www.zlib.net/zpipe.c[zpipe -d], albo wpisz: + +$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d + +polecenie to pokaże ci zawartość objektu jako tekst. + +=== 'Trees' === + +Gdzie są więc nazwy plików? Przecież muszą być gdzieś zapisane. Podczas wykonywania 'commit' Git troszczy się o nazwy plików: + +$ git commit # dodaj jakiś opis. $ find .git/objects -type f $ find .git/objects -type f + +Powinieneś ujrzeć teraz 3 objekty. Tym razem nie jestem w stanie powiedzieć, jak nazywają sie te dwa nowe pliki, ponieważ częściowo są zależne od nazwy jaką nadałeś plikom. Pójdźmy dalej, zakładając, że jedną z tych danych nazwałeś ``rose''. Jeśli nie, możesz zmienić opis, by wyglądał jakby był twój: + +$ git filter-branch --tree-filter 'mv TWOJA_NAZWA rose' $ find .git/objects -type f + +Powinnaś zobaczyć teraz plik +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, ponieważ jest to klucz SHA1 jego zawartości. + +"tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d + +Sprawdź, czy plik na prawdę odpowiada powyższej zawartości przez polecenie: + +$ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch + +Za pomocą zpipe łatwo sprawdzić klucz SHA1: + + +$ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum + +Sprawdzanie za pomocą 'cat-file' jest troszeczkę kłopotliwe, bo jego output zawiera więcej niż tylko nieskomprymowany objekt pliku. + +Nasz plik to takzwany objekt 'tree': lista wyrażeń, na którą składają się rodzaj pliku, jego nazwa i jego klucz SHA1. W naszym przykładzie typ pliku to 100644, co oznacza, że `rose` jest plikiem zwykłym, natomiast klucz SHA1 odpowiada kluczowi SHA1 objektu 'blob' zawierającego zawartość `rose`. Inne możliwe rodzaje plików to programy, linki symboliczne i katalogi. W ostatnim przypadku klucz SHA1 wskazuje na objekt 'tree'. + +Jeśli użyjesz polecenia 'filter-branch', otrzymasz stare objekty, które nie są już używane. Mimo iż automatycznie zostaną usunięte po upłynięciu okresu łaski, chcemy się ich pozbyć od zaraz, aby lepiej prześledzić następne przykłady. + +$ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune + +W prawdziwych projektach powinnaś unikać takich komend, poonieważ zniszczą zabezpieczone dane. Jeśli chcesz posiadać czyste repozytorium, to najlepiej załóż nowy klon. Bądź też ostrożna przy bezpośredniej manipulacji +.giz+: gdy równocześnie wykonywane jest polecenie Git i zgaśnie światło? Generalnie do kasowania referencji powinnaś używać *git update-ref -d*, nawet gdy ręczne usunięcie +ref/original+ jest dość bezpieczne. + +=== 'Commits' === + +Wytłumaczyliśmy dwa z trzech objektów. Ten trzeci to objekt 'commit' Jego zawartość jest zależna od opisu 'commit' jak i czasu jego wykonania. By wszystko do naszego przykładu pasowało, musimy trochę pokombinować. + +$ git commit --amend -m Shakespeare # Zmień ten opis. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Zmanipuluj znacznik czasowy i nazwę autora. $ find .git/objects -type f $ find .git/objects -type f + +Powinieneś znaleźć +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+, co odpowiada kluczowi SHA1 jego zawartości: + +"commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice 1234567890 -0800" LF "committer Bob 1234567890 -0800" LF LF "Shakespeare" LF + +Jak i w poprzednich przykładach możesz użyć 'zpipe' albo 'cat-file' by to sprawdzić. + +To jest pierwszy 'commit', przez to nie posiada rodziców 'commit'. Następujące 'commits' będą zawsze zawierać przynajmniej jedną linikę identyfikującą rodzica. + +=== Nie do odróżnienia od magii === + +Tajemnice Gita wydają się być proste. Wygląda to jak połączenie kilku skryptów, troszeczkę kodu C i w przeciągu kilku godzin jesteśmy gotowi: zmiksowanie podstawowych operacji na systemie danych obliczenia SHA1, przyprawione danymi blokującymy i synchronizacją dla stabilności. W sumie można by tak opisać najwcześniejsze wersje Gita. Tym niemniej, abstrahując od udanych trików pakujących, by oszczędnie odnosić się z pamięcią i udanych trików indeksujących by zaoszczędzić czas, wiemy jak Git sprawnie przemienia system danych i bazę danych, co jest optymalne dla kontroli wersji. + +Przyjmijmy, gdy jakikolwiek plik w objektowej bazie danych ulegnie zniszczeniu poprzez błąd nośnika, to jego SHA1 nie będzie zgadzać i jego zawartością, co od razu wskaże nam problem. Poprzez tworzenie kluczy SHA1 z kluczy SHA1 innych objektów, osiągniemy integralność danych na wszystkich poziomach. 'commits' są elementarne, do znaczy, 'commit' nie potrafi zapamiętać jedynie części zmian: klucz SHA1 'commit' możemy obliczyć i zapamiętać dopiero po tym gdy zapamiętane zostały wszystkie objekty 'tree', 'blob' i rodziców 'commit'. Objektowa baza dynch jest osporna na nieoczekiwane przerwy, jak na przykład przerwanie dostawy prądu. + +Możemy przetrwać nawet podstępnego przeciwnika. Wyobraź sobie,, ktoś ma zamiar zmienić treść jakiegoś pliku, która leży w jakiejś starszej wersji projektu. By sprawić pozory, że baza danych wygląda nienaruszona. musiałabyś wmienić klucze SHA1 korespondujących objektów, ponieważ plik zawiera teraz zmieniony sznur znaków. To znaczy róniweż, że musiałabyś zmienić każdy klucz objektu 'tree', które ją referują oraz w wyniku tego wszystkie klucze 'commits' zawierające obejkty 'tree' dodatkowo do pochodnych tych 'commits'. Oznacza to również, że klusz oficjalnego HEAD różni się od klucza HEAD manipulowanego rapozytorium. Wystrarczy teraz prześledzić ścieżkę różniących się kluczy SHA1, odnajeźć okaleczony plik, jak i 'commit' w którym po raz pierwszy wystąpił. + +Krótko mówiąc, doputy reprezentujące ostatni commit 20 bajtów są zabezpieczone, przefałszowanie repozytorium Gita nie jest możliwe. + +A co ze sławnymi możliwościami Gita? +'Branching'? 'Merging'? 'Tags'? To szczegół. Aktualny HEAD przetrzymywany jest w pliku +.git/HEAD+, która posiada klucz SHA1 ostatniego 'commit'. Klucz SHA1 zostaje aktualizowany podczas wykonania 'commit', tak samo jak i przy wielu innych poleceniach. 'branches' to prawie to samo, są plikami zapamiętanymi w +.git/refs/heads+. 'Tags' również, znajdziemy je w +.git/refs/tags+, są one jednak aktualizowane poprzez serię innych poleceń. \ No newline at end of file diff --git a/tmp/translate.txt b/tmp/translate.txt new file mode 100644 index 0000000..3121c9b --- /dev/null +++ b/tmp/translate.txt @@ -0,0 +1,17 @@ +== Załącznik B: Przetłumaszyć to HOWTO == + +Aby przetłumaszyć mije HOWTO polecam wykonanie następujących ponniżej kroków, wtedy moje skrypty będą w prosty sposób mogły wygenerować wersje HTML i PDF. Poza tym wszystkie tłumaszenia mogą być prowadzone w jednym repozytorium. + +'Clone' texty źródłowe, następnie utwórz katalog o nazwie skrótu IETF przetłumaszonego języka: sprawdź http://www.w3.org/International/articles/language-tags/Overview.en.php[Artykół W3C o internacjonalizacji]. Na przykład, angielski to "en", a japoński to "ja". Skopiuj wszystkie pliki +txt+ z katalogu "en" do nowoutworzonego katalogu. + +Aby przykładowo przetłumaczyć to HOWTO na http://de.wikipedia.org/wiki/Klingonische_Sprache[Klingonisch], musisz wykonać następujące polecenia: + +$ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" jest skrótem IETF języka Klingonisch. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Przetłumacz ten plik. + +i zrób to z każdą następną daną textową. + +Edytuj Makefile i dodaj skrót języka do zmiennej `TRANSLATIONS`. Teraz możesz swoją pracę w każdej chwili sprawdzić: + +$ make tlh $ firefox book-tlh/index.html + +Używaj często 'commit' a gdy już skończysz, to daj znać. GitHub posiada interfejs, który to ułatwi: utwórz twój własny 'Fork' projektu "gitmagic", 'push' twoje zmiany i daj mi znać, by je 'mergen'. \ No newline at end of file -- 2.11.4.GIT