diff --git a/README.md b/README.md index 42d0a16..33d4fd1 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,62 @@ ## Foreword -Many of us using git in our day-to-day work. I'm noticed that some of you (or may be many of you) just use few features of it. -Like: `add`, `commit`, `push` and `pull`. -There is might be different reasons for that. -You don't know another stuff and/or afraid to use them. -Commands `amend` and `rebase` is nightmare for you. +Many of us are using git in our day-to-day work. I've noticed that some of you (or may be many of you) are using only a few few features of it e.g. `add`, `commit`, `push`, `pull`. +There might be different reasons for that; you might not know anything better or are overwhelmed by their complexity. +Commands `amend` and `rebase` might seem a nightmare for you. Now I will try to fix that. ## Why history is important -Git history it's like contents of the book. -It helps other people to understand the purpose of each commit. -What's why your logs should be well named and followed by clear description. +Git history is like the table of contents of a book. +It helps other people understand the purpose of each commit. +What's why your logs should be well named and follow a clear description. ## Some Commits best practices * Commit should contain related changes. Fixing two different bugs should produce two separate commits. Small commits make it easier to understand for other team members. - * Commit often. It will allows you to commit only related changes. + * Commit often. It will allow you to commit only related changes. - * Don't commit half-done work. This means you should not commit unfinished feature at the end of working day. - This also does not means you should commit whole huge feature in one commit. Just split it into small chuncks. + * Don't commit work half-done. This means you should not commit an unfinished feature at the end of working day. + This also does not mean you should commit a huge complex feature in one commit. Just split it into small chuncks. - * Use branch per feature. That allow you group commits in history. + * Use branch per feature. That allows you to group the commits in history. ## Read before commit -There is a good practice to read your emails and messages before send them. -That's works and with `git diff` too. So check your diffs before commit. +There is a good practice to read your emails and messages before you send them. +That works and with `git diff` as well. So check your diffs before commit. -Just use `git diff` command for unstaged files and `git diff --cached` for staged. -(staged files are those files what just have added using `git add` command) +Simply use `git diff` command for unstaged files and `git diff --cached` for the staged ones. +(staged files are the files what just have added using `git add` command) -I'm usually find some stuff to fix like extra empty line, todo comment or typos. +I usually find some stuff to fix like extra empty line, todo comment or typos. ## How to write good commit message Never use -m (--message) flag to git commit. -It invites you set short unreadable commit messages. -`git commit` will opens your default editor where you could write more useful commit message. +It invites you to set short unreadable commit messages. +`git commit` will open your default editor where you could write a more useful commit message. -There is terrific note from Tim Pope about writing commit messages. +There is a terrific note from Tim Pope about writing commit messages. Long story short. -Your commit title should be short, capitalized title with 50 or less characters followed by blank line. -Then goes more detailed description of changes wrapped in about to 72 characters. +Your commit title should be a short, capitalized sentence with 50 or less characters followed by a blank line. +Then a more detailed description of changes goes wrapped in about to 72 characters. -Write your commit messages in imperative way: Use "fix", "change", "add" instead of "fixed", "changed", "added". -That convention matches up with commit messages generated by `git merge` and `git revert`. +Write your commit messages in an imperative fashion: Use "fix", "change", "add" instead of "fixed", "changed", "added". +This convention matches up with commit messages generated by `git merge` and `git revert`. ## Add changes interactively -You're probably familiar with adding files into staging area using `add` command. -Sometimes you might make changes whose should belong to different commits. -As we have talk before we don't want to our commits to looks like 'Fixed A and B'. +You're probably familiar with adding files into staging area using the `add` command. +Sometimes you might make changes that should belong to different commits. +As we said, we don't want our commits to looks like 'Fix A and B'. So here `add -p ` (`add --patch`) comes to help. -Interactive adding will asks to perform some action for each hunk. +Interactive adding will ask to perform some action for each hunk. These actions are: y - stage this hunk @@ -84,7 +82,7 @@ We could avoid using fast forward for merge and even set is as default behavior ## Merge with --no-ff -The --no-ff flash ensures there will be a merge commit, even if git could do a fast forward merge. +The --no-ff flag ensures there will be a merge commit, even if git could do a fast forward merge. That picture shows the difference between merges with and without fast forward. http://i.stack.imgur.com/FMD5h.png @@ -100,13 +98,13 @@ Clear enough? NO! Simply put: Rebase is the command to rewrite history in some ways. -Whese ways could be: Moving your commits on top of another branch, squash, split, amend. +These ways could are: moving your commits on top of another branch, squash, split, amend. Simpliest use: From your branch `git rebase master`. Saves all commits made in current branch to temporary area. -Resets your branch to master state then apply commits one by one from temporary area. +Resets your branch to master state then applies commits one by one from temporary area. Complete result would looks like: [rebase picture] @@ -134,23 +132,23 @@ It will opens default editor with following result: # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. -But. Do not rebase published commits. Rebase changes commits and these commits will differs from previous ones. +But. Do not rebase published commits. Rebase changes commits and these commits will differ from previous ones. People who've got these commits before will face some issues after pulling your changes. -As exception I accept rebasing for feature branches. Because I will throw it out after merge into development branch. +As exception I accept rebasing for feature branches because I will throw it out after merge into the development branch. ## git pull --rebase Pulling changes from remote location by default tries to merge changes into you current branch. -It's use fast forward if it's possible but creates merge commit when faces conflicts in changes. -Avoid extra merge commit we can use git pull with --rebase params. -That will move our non published commits on top of history tree. +It uses fast forward if it's possible but creates merge commit when facing conflicts in changes. +In order to avoid extra merge commit we can use git pull with --rebase params. +That will move our non published commits on top of the history tree. ## My Git workflow * Create new branch for every main feature using git checkout -b * Commit your code in small indepent pieces. * If you forgot something for last commit use `git --amend` - * If you forgot something for recent commits then commit is as fix and use `rebase -i` to change commits order and squashing them. + * If you forgot something for recent commits then commit is as fix and use `rebase -i` to change commits order and squash them. * Use git rebase (it might be master or develop) from your to move all your commits on top. * Use git merge --no-ff from @@ -171,10 +169,10 @@ To avoid wasting time you could describe aliases to most used git commands. Ther rc = rebase --continue ``` -The most attentive of you might notice what I'm using file name as `l` alias. -It's how you could get result of external file as your command. +As you could notice I'm using file name as `l` alias. +This is how you can get the result of an external file as your command. -This is my script (To be honest it's stolen from another guy) to format logs +This is my script (To be honest it's 'borrowed' from another guy) that formats logs: https://github.com/ck3g/dotfiles/blob/master/githelpers @@ -185,11 +183,11 @@ Here is how it looks like in terminal window. # Bonus feature ## Git Bisect -One of my favorite feature. It helped me many times. I'm also like to ask about it on job inverviews. +One of my favorite features that helped me many times. I also enjoy asking about it on job inverviews. -Lets imagine a situation when you realized what something was broken and you don't know when. -But you're know what 15 commits ago it's used to work. How to find broken commit? -What when git bisect helps. +Lets imagine a situation where you have realized that something had broken and you don't know when. +But you know that 15 commits ago it used to work. How to find the guilty commit? +That's when git bisect helps. git bisect uses binary search to find broken commit. @@ -201,17 +199,16 @@ git bisect good v1.2.3 # Mark good commit ``` git will checkout you between these commits where you will be able to check your current state. -Then mark commit as good/bad. You should keep going until you're find broken commit. -Check the difference using git show. Understand the problem. And use git reset to finish. +Then mark commit as good/bad. You should keep going until you have found the bad commit. +Check the differences using git show. Understand the problem. Use git reset to finish. Note: -When your commits isn't atomic, like failing specs and its fixes are in different commits. -You will probably be confused then bisect provide you commit with failing specs. +When your commits aren't atomic e.g. failing specs and its fixes are in different commits, you will probably be confused that bisect provides the commit with the failing specs. ## Afterword -Git is a powerful tool. We all using it on every day basis. Thus it's a good reason to master it. -Try to use these advices in your everyday work. I hope you will like it. +Git is a powerful tool. We are all using it on every day basis. Thus it's a good idea to master it. +Try to use these pieces of advice in your everyday work. I hope you will like it. ## Links diff --git a/README.ru.md b/README.ru.md new file mode 100644 index 0000000..02cf92a --- /dev/null +++ b/README.ru.md @@ -0,0 +1,208 @@ +## Вступление + +Многие из нас используют git в своей повседневной работе. Я обратил внимание, что некоторые из Вас (а, может быть, и многие) используют только несколько возможностей git, такие как add, commit, push и pull. +Это может быть по нескольким причинам. Вы можете не знать дополнительных возможностей или вы напуганы их сложностью. +А такие команды, как amend и rebase - для вас, вообще, ночной кошмар. +Сейчас попробуем разобраться в этом всем. + +## Почему история коммитов важна + +История коммитов - это как оглавление в книге. +Это помогает вашим коллегам понимать содержимое и цель каждого коммита. +Обнаружив некоторую ошибку или непонятную часть кода, каждый пользователь может выснить, в каком именно коммите были сделаны те или иные изменения, а правильное описание сильно поможет ему в этом. + +## Полезные советы + + * Коммит должен содержать только те изменения, которые к нему относятся. Исправляя две разные ошибки, вы должны разделить их в отдельные коммиты. Небольшие коммиты проще понять всем остальным. + * Частые коммиты. Это поможет Вам создавать небольшие коммиты с одной определенной задачей. + * Не стоит коммитить незаконченный функционал. Это значит - не нужно создавать коммит в конце рабочего дня лишь для того, чтобы "не потерять" свои изменения. Гит - не система для бэкапов. И это вовсе не значит, что нужно коммитить всю задачу (feature) целиком. Нужно просто разбить ее на подзадачи. + * Используйте отдельную ветку для каждой задачи. Это также поможет выглядеть истории более понятно. + +## Перечитывайте перед тем, как коммитить. + +Перечитывать написанное всегда полезно, будь то письмо, сообщение или сочинение. Это помогает выявить некоторые ошибки и опечатки. +То же самое при работе с git. + +git diff в этом случае отобразит разницу в измененных файлах, а git diff -- cached в файлах, уже добавленных командой git add. +Просматривая свои изменения, я обычно нахожу несколько вещей, которые можно исправить: лишняя пустая строка, комментарий или опечатки. + +## Как составить хорошее описание коммита. + +Не используйте параметр -m (--message), когда коммитите ваши изменения. Это подталкивает Вас написать короткое и, зачастую, мало полезное описание коммита. +git commit же (без параметров) запустит ваш редактор по умолчанию, где можно более удобно редактировать описание. + +Есть замечательная заметка Тима Поупа о том, какие должны быть описания коммитов. + +Короче говоря: + +Заголовок коммита должен быть коротким описанием, начинающийся с заглавной буквы, не более 50 символов. +Затем следует более детальное описание, отделенное от заголовка пустой строкой. Границы данного описания должны быть в пределах 72 символов. + +Пишите Ваши сообщения в повелительном наклонении: Использя "fix", "change", "add" вместо "fixed", "changed", "added". +Это соглашение совпадает с сообщениями, генерируемыми командами git merge и git revert. + +## Используйте возможность добавлять изменения по шагам. + +Вероятнее всего, вы знакомы с добавлением изменений в staging область, используя комманду git add. +Иногда Ваши изменения могут принадлежать разным коммитам. +Как мы говорили ранее, мы не хотим видеть коммитов подобных: "Fix A and B". + +В такие моменты к нам на помощь приходит атрибут `git add -p ` (`add --patch`). +Данная команда позволяет добавлять наши изменения по шагам, останавливаясь на каждом кусочке и предлагая следующие действия на выбор: + + y - stage this hunk + n - do not stage this hunk + q - quit; do not stage this hunk nor any of the remaining ones + a - stage this hunk and all later hunks in the file + d - do not stage this hunk nor any of the later hunks in the file + g - select a hunk to go to + / - search for a hunk matching the given regex + j - leave this hunk undecided, see next undecided hunk + J - leave this hunk undecided, see next hunk + k - leave this hunk undecided, see previous undecided hunk + K - leave this hunk undecided, see previous hunk + s - split the current hunk into smaller hunks + e - manually edit the current hunk + ? - print help + + +## Что такое fast forward + +Когда вы пытаетесь слить одну ветку в другую и это слияние не вызывает никаких конфликтов в измененном коде, git просто меняет текущий указатель на последний коммит целевой ветки. Этот процесс и называется fast forward. +Но мы можем избегать такого поведения при слиянии веток. + +## Слияние, используя --no-ff + +Параметр --no-ff позволят создавать дополнительный коммит при слиянии веток даже там, где слияние может быть произведено через fast forward. +Разница между слиянием с fast forward и без представлена на рис: + +http://i.stack.imgur.com/FMD5h.png + +## git rebase + +Что такое rebase? + +Из справки о git: + + "git-rebase - Forward-port local commits to the updated upstream head" + +Достаточно понятное описание? + +Конечно, нет. + +Rebase -- это команда, которая позволяет переписывать историю некоторыми методами. +Такими, как: поренос коммитов в начало другой ветки, слияние коммитов и их изменение. + +Самое простое применение: + +Находясь в вашей ветке `git rebase master`. +Сохраняет ваши коммиты во временной области. +Сбрасывает вашу ветку до состояния master, потом применяет по одному Ваши коммиты из временной области. +Конечный результат выглядит следующим образом: [rebase picture] + +Командой `git rebase --interactive (-i) commits..range` можно провести интерактивный rebase. +Данная команда запустит наш редактор со следущим содержанием: + + pick 1fc6c95 Patch A + pick 6b2481b Patch B + pick dd1475d something I want to split + pick c619268 A fix for Patch B + pick fa39187 something to add to patch A + pick 4ca2acc i cant' typ goods + pick 7b36971 something to move before patch B + + # Rebase 41a72e6..7b36971 onto 41a72e6 + # + # Commands: + # p, pick = use commit + # r, reword = use commit, but edit the commit message + # e, edit = use commit, but stop for amending + # s, squash = use commit, but meld into previous commit + # f, fixup = like "squash", but discard this commit's log message + # x, exec = run command (the rest of the line) using shell + # + # If you remove a line here THAT COMMIT WILL BE LOST. + # However, if you remove everything, the rebase will be aborted. + +Но не рекомендуется применять rebase к уже опубликованным коммитам. Rebase изменяет затронутые коммиты, даже если эти изменения не очевидны. Это значит, что люди, которые уже стянули эти коммиты ранее, столкнутся с различными сложностями, когда попытаются стянуть новые изменения. +В качестве исключения, я допускаю rebase в отдельной ветке (для одной подзадачи), т.к. эта ветка в дальнейшем потом все равно будет удалена после слияния ее в основную ветку. + +## git pull --rebase + +git pull, по умолчанию, пытается слить изменения (используя merge) в текущую ветку. +Как мы уже знаем, merge испольует fast forward для слияния там, где это возможно, но создает дополнительный коммит при возникновении конфликтов. +Для того, чтобы этого избежать, мы можем использовать git pull --rebase. +Эта команда сдвинет наши неопубликованные коммиты вверх дерева истории. + +## Как выглядит (мой) рабочий процесс. + + * Для каждой основной задачи создается отдельная ветка, используя git checkout -b <имя-ветки> + * Создаются коммиты для каждой небольшой и независимой подзадачи + * Если что-то забыли, можно подправить последний коммит git --amend + * Если изменения нужно провести в нескольких коммитах, поменять их местами или объединить, используем git rebase -i + * Перед слиянием текущей ветки в основную производим git rebase <имя-основной-ветки> + * Сливаем ветки, используя git merge --no-ff <имя-ветки>, находясь при этом в основной ветке + +## Aliases + +Для того, чтобы сократить время набора различных комманд, можно использовать их сокращенные псевдонимы. + +``` +# ~/.gitconfig + +[alias] + st = status + co = checkout + aa = add --all + ff = flow feature + l = !~/.githelpers + dc = diff --cached + rc = rebase --continue +``` + +Можно заметить, что мой `l` псевдоним использует отличную от других структуру. Таким образом, можно использовать результат выполнения внешнего файла. + +Вот пример моего скрипта для вывода дерева истории. (По правде говоря, я позаимствовал его у одного разработчика) + +https://github.com/ck3g/dotfiles/blob/master/githelpers + +Вот как он выглядит в окне терминала. + +(Add screen here) + +# Бонус +## git bisect + +Одна из моих любимых возможностей, которая ни раз меня выручала. Также мне нравится задавать этот вопрос на интервью. + +Представим следующую ситуацию. Мы заметили, что у нас сломался какой-то функционал, мы не заметили, когда это случилось, но точно знаем, в какой из версий этот функционал работал. Как узнать, в каком из коммитов это произошло? + +git bisect использует двоичный поиск для того, чтобы найти этот самый коммит. + +``` +git bisect start +git bisect bad # Помечаем текущую версию, как сломанную +git bisect good v1.2.3 # Почаем коммит с работающей версией +``` + +git будет переключать нам по одному коммиту, где мы будем прогонять тесты или каким-либо образом проверять, рабочий коммит или нет. +В зависимоти от результата, мы также отмечаем текущий коммит, как сломанный или нет, до тех пор, пока не найдем виновника. +Далее коммандой git show мы можем просмотреть текущий коммит и выяснить причину. +git reset позволяет завершить процесс поиска. + +В данном случае вы можете столкнуться со следующей сложностью. Если ваши коммиты не атомарны, например, в одном коммите содержатся проваленные тесты, а в следующем -- их реализация, то git bisect может привести вас к первому коммиту, что, при запуске тестов, введет вас в заблуждение. Вы не будете понимать, что именно у вас сломалось. Это одна из причин, почему я не рекомендую разделять таким образом тесты и реализацию в разные коммиты. + +## Заключение + +Git -- мощный инструмент. Т.к мы используем его каждый день, то овладеть им лучше будет довольно неплохой затеей. +Попробуйте использовать эти техники в своей работе. +Надеюсь, это принесет вам удовольствие от вашей работы. + +## Дополнительные материалы + + + * http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html + * http://mislav.uniqpath.com/2013/02/merge-vs-rebase/ + * https://www.atlassian.com/git/tutorials/rewriting-history + * http://code.tutsplus.com/tutorials/git-tips-from-the-pros--net-29799 + * http://codeinthehole.com/writing/pull-requests-and-other-good-practices-for-teams-using-github/