This fragment is about to be reported (you'll remain on this page): You can enter a comment to clarify the mistake if you would like to: |
Последние несколько дней я основательно познакомился с набирающей обороты новой системой контроля версия – она же git и её вёб-хранилище – GitHub.com.
До сих пор я знал только одну VCS – Subversion, с которой работал последние три года. Как оказалось, по сравнению с git (да и с Mercurial, судя по всему, тоже) она крайне простая и… не хочу сказать примитивная, потому что свои задачи она решает более чем успешно – скорее просто VCS с другим принципом работы (начать с того, что она централизованная, как CVS) При первом же знакомстве git меня удивил тем, что большинство операций там делается через командную строку – собственно, набор утилит git под Windows – ничто иное, как портированный bash с основными утилитами Unix. При этом GUI-инструменты git (например, SmartGit) мало того, что просто интерфейсы к git.exe, так ещё и понять, что именно они делают можно только уже познакомившись с работой с git из командной строки.
Во всяком случае таковы мои первые впечатления. Subversion благодаря всей своей простоте вообще не требует особого обучения, и уж тем более работы с командной строкой – благо там всего две команды, которые крайне нужны для работы – commit и update, и то первая вызывает последнюю, поэтому начать можно вообще не особо напрягаясь.
В общем, ладно. В этом посте я хочу записать основные команды git и заморочки, которые запомнил, пока с ними разбирался. Хотя вообще, если вас интересует матчасть – можете сразу перейти к ней.
Допустим, вы зарегистрировались на GitHub и нажали на кнопку «New repository». После ожидания в несколько секунд появится сообщение с командами, которые нужно выполнить, чтобы внести в хранилище первую ревизию. Здесь и началось веселье…
shSet up git git config --global user.name "Pavel" git config --global user.email proger.xp@gmail.com Next steps: mkdir test-repo cd test-repo git init touch README git add README git commit -m 'first commit' git remote add origin git@github.com:ProgerXP/test-repo.git git push -u origin master Existing Git Repo? cd existing_git_repo git remote add origin git@github.com:ProgerXP/test-repo.git git push -u origin master ...
Команды разделены на три части, пройдёмся по каждой из них.
Здесь нужно настроить своё подключение. Для авторизации GitHub использует SSH-ключи (я так понял родной протокол git:// – вообще гибрид или надстройка над SSH). Это требуется сделать только один раз после регистрации, а не каждоый раз после создания нового хранилища.
Как создать свой SSH-ключ описано в статье, ссылка на которую есть в списке команд – здесь у меня не возникло никаких проблем. Замечу только, что всё делается в командной строке bash, устанавливаемой вместе с версией git под Windows.
Две строчки
shgit config --global устанавливают настройки по умолчанию для всех подключений git в данной системе.
Теперь начинается самое интересное – но сперва немного матчасти, иначе будет ничего не понятно (мне её пришлось узнавать экспериментально, поэтому не ручаюсь, что всё так, как есть на самом деле).
Перво-наперво, git (и Mercurial) является распределённой системой контроля версий – как противоположность централизованным системам VCS – SVN и CVS. На практике это значит, что вместо обращения к хранилищу где-то на сервере у вас есть своё локальное хранилище – в полном смысле этого слова: с полной историей коммитов, branch, tags и т.п.
Вся система поддерживается в едином виде с помощью синхронизации локальных хранилищ с каким-то центральным – на сервере (ведь если вы работаете в команде, то у каждого её члена есть точно такая же копия общего хранилища, где хранятся его собственные коммиты – их ведь не видно в вашей, автономной копии). В этом сходство с SVN, но разница в том, что серверное хранилище – точно такое же локальное, как и у вас, тогда как в SVN у вас хранится просто последняя версия изменений, которая обновляется при выполнении
shsvn update или
shsvn commit.
Как мы видим, серверные хранилища git весьма формально являются «центральными» (то есть едиными) – каждый может её скачать себе в систему и у него будет точно такой же клон. Или форк, если он начнёт вносить туда изменения, но не будет синхронизироваться с сервером.
Матчасть закончилась, но перед продолжением нужно сказать пару слов о терминологии git – у меня первое время из-за непонимания всех этих «pull» и «remote» голова кругом шла. Итак:
Так, пожалуй теперь с матчастью точно закончено. Приступим к разбору того, как создаётся новое хранилище.
shmkdir test-repo cd test-repo git init touch README git add README git commit -m 'first commit' git remote add origin git@github.com:ProgerXP/test-repo.git git push -u origin master
Первые две команды – самые простые и одинаково знакомы как пользователям Windows, так и *nix. Сначала мы создаём папку для нового хранилища (
shmkdir test-repo), затем переходим в неё (
shcd test-repo).
Следующая команда (
shgit init) инициализирует (создаёт) новое хранилище в текущей папке. Фактически с этого момента у вас уже есть полноценное хранилище git, в котором пока только одна ветка – master – и ни одного коммита (push).
Далее мы создаём файл README – для git, как и для *nix-систем вообще, свойственно помещать в папку программы файл с таким именем и без расширения. Там же могут быть LICENSE, COPYRIGHT и прочие.
GitHub поддерживает язык разметки Markdown – расширенный до так называемого GFM – GitHub Flavored Markdown (пусть кто-то другой попытается перевести это на русский). Текстовые файлы в этой разметке обычно имеют расширение .md, реже – .markdown. GitHub Автоматически форматирует файлы с этими расширениями в HTML при просмотре через вёб-интерфейс, поэтому есть смысл создавать наш файл именно в этом формате – README.md.
Итак, мы делаем либо
shtouch README, либо
shtouch README.md – это даёт нам новый пустой файл, в который мы записываем инструкцию по установке, лицензию, ссылки на полезные страницы и прочее.
Как только это сделано нам нужно совершить коммит –
shgit commit -m 'first commit'. В отличии от SVN коммитом здесь считается внесение данных в собственное локальное хранилище – как уже должно было стать понятно из матчасти. Флаг -m и текст в кавычках в этой команде задают текст сообщения для коммита – если они опущены, git откроет консольный текстовый редактор типа vim, который может быть мягко говоря неудобным для тех, кто к таким не привык.
Мельком упомянем две команды, которых нет в списке выше: git diff и git status. Первая показывает изменения в текущей структуре файлов и папок по сравнению с последним коммитом, а вторая просто выводит два списка файлов:
Так вот, перед коммитом нужно сначала добавить файлы в список коммита – в TortoiseSVN всё это (и сообщение коммита, и добавление файлов, и просмотр их изменений) делается в одном окне, но так как у нас в распоряжении только командная строка такой роскоши у нас нет. Добавляются файлы командой git add – причём она рекурсивна и принимает файловые маски. В списке команд выше мы делаем
shgit add README, но точно так же могли бы сделать
shgit add * – это бы внесло в список коммита все файлы в текущей папке.
Итак, файлы добавлены (git add), коммит сделан (git commit). Что теперь?
Заметим, что все изменения до сих пор у нас вносились только в локальное хранилище – у него нет совершенно никакой связи с тем, что перед этим мы создали на GitHub. Мы сделали git init, но это создало нам наше собственное локальное хранилище, куда мы и внесли README.
Поэтому сейчас мы добавим связь с главным хранилищем и «толкнём» в него наши изменения:
shgit remote add origin git@github.com:ProgerXP/test-repo.git git push -u origin master
Адрес хранилища (git@github.com:...) у вас будет свой, в остальном всё так же. Что здесь происходит?
shgit push.Всё, теперь на сервере GitHub появятся наши изменения и первая ревизия. С этого момента хранилище полностью работоспособно.
Все будущие коммиты делаются точно так же – сначала меняются нужные файлы, затем они добавляются в список коммита командой
shgit add * или индивидуально с помощью
shgit add dir/file.ext, затем совершается коммит (
shgit commit) и наконец изменения «толкаются» в основное хранилище (
shgit push если вы указали флаг -u, см. выше).
Обратите внимание, что вы можете «толкать» изменения не обязательно после каждого коммита – это уже ваше дело. Они просто не будут видны на сервере, пока вы не выполните git push. А с вашей локальной копией работают все обычные команды – например, git log.
Также заметьте, что после начального выполнения git add remote этого больше делать не нужно, если только вы не перемещаете удалённое хранилище на другой адрес.
shcd existing_git_repo git remote add origin git@github.com:ProgerXP/test-repo.git git push -u origin master
Я не буду снова переписывать написанное чуть выше – фактически, здесь вы делаете то же самое, что и при создании нового репозитария, только пропуская все шаги до добавления новой связи с сервером:
shcd existing_git_repo – перейти в папку со старым хранилищем, которое нужно импортировать в новое хранилище на GitHub;shgit remote add origin ... – добавить связь между этим хранилищем и адресом («…») и присвоить ей имя origin (можно и любое другое, которое вы затем будете указывать в
shgit push);shgit push -u origin master – отправить изменения из текущего хранилища в хранилище с именем связи «origin» и ветки «master». -u говорит, что нужно использовать эту форму вызова, если в дальнейшем будет вызываться git push (без параметров).Здесь я опишу команды, которые мне пригодились, пока я развлекался с созданием нового «pull request». Справку о каждой подкоманде или обо всех командах в целом можно получить по аналогии с svn help – git help [command]. В отличии от svn, справка git открывается в браузере по умолчанию.
Подкоманда git branch работает с ветками текущего хранилища. Вот, что она умеет делать (как обычно, наберите git help branch для полного описания):
shgit branch – вывести список всех локальных веток; напротив текущей ветки слева будет стоять звёдночка (*).shgit branch -a – ключ -a выводит не только локальные ветки, но и удалённые (remotes) – тёмно-красным цветом.shgit branch -m NEW OLD – переименование ветки из «old» в «new». Полезно, если вы ошиблись в имени при её создании. Есть ещё команда -M, но она как-то иначе относится к спорным ситуациям – в справке написано, как именно.shgit branch -d NAME – удаление ветки. Есть форма -D, но я не знаю, чем она отличается.shgit push [-u] ORIGIN :NAME – когда ветка удаляется из локального хранилища она по прежнему остаётся на сервере и удалить его с помощью git push не получится, так как оно уже отсутствует в локальной версии. Для удаления ветки и локально, и удалённо и используется эта форма подкоманды git push, где имя ветки начинается с двоеточия.shgit push [-u] ORIGIN :BRANCH_1 :BRANCH_2 ... – можно «толкнуть» несколько веток одновременно, просто перечислив их в конце команды – работает как на удаление веток, так и на обычное обновление.Подкоманда git remote работает со связями локального хранилища с удалёнными. Её формы:
shgit remote – выводит список связей – только их имена.shgit remote show NAME – показывает информацию о связи по её имени.shgit remote add NAME git@... – добавляет новую связь с переданным адресом и присваивает ей имя «NAME». После этого команда git push и другие смогут загружать содержимое в хранилище по этому адресу. Заметьте, что хоть подкоманда и называется «remote» хранилище может быть локальным – начинаться с протокола file://. Другие протоколы также допустимы – https://, git:// и т.д.shgit remote rm NAME – удаляет связь.Подкоманда git commit фиксирует локальные изменения в хранилище, создавая новую ревизию (в терминах SVN).
shgit commit -s -m "Сообщение." – флаг -s «подпишет» ваш коммит – добавит в конец сообщения строку вида Signed-off-by: Your Name <your@email.com>. Это часто используется вместо флажка «Принять лицензионное соглашение» в проектах, где для выполнения коммита нужно подтвердить, что, скажем, вы согласны распространять свой код под лицензией проекта. Текст соглашения можно увидеть, например, в хранилище Laravel в разделе «Developer’s Certificate of Origin».shgit commit -m "$var" – обратите внимание, что в правилах *nix – обрабатывать строки в кавычках (не в апострофах) и заменять там переменные среды, которые начинаются на $ в отличии от Windows, где используется форма %VARIABLE%. Из-за этого из сообщения коммита вида Added $default parameter for function(). удалится строка $default, либо заменится на её значение, если определена переменная среды с таким именем.shgit commit --amend -m "Сообщение." ... – ключ --amend изменяет последний коммит вместо создания нового. Полезно для изменения сообщения в логе, хотя может быть использовано и для изменения списка файлов.Подкоманда git checkout выгружает файлы из текущей ревизии хранилища в нужную папку. Пока я пользовался всего одной её формой:
shgit checkout -b NEW OLD – создаёт новую ветку, копируя уже существующую, и «переключает» на неё текущую папку (ветка становится активной, что видно в
shgit branch).Подкоманда git stash очень полезна, когда хочется начать с чистого листа (откатить все локальные изменения до последнего коммита), но в то же время не потерять их. После «утаивания» изменений (stashing away) git перенесёт их все в отдельную ветку, которую позже можно восстановить или удалить.
shgit stash – сохранить все изменения и откатить локальную копию.shgit stash list – показать все сохранённые изменения.shgit stash show – показать сохранённые изменения с именами файлов в них.shgit stash pop – вернуть последнее сохранённое изменение в локальное дерево.shgit status – показать список имён файлов, которые были изменены (удалены, добавлены или другое содержимое).shgit diff – похожа на status, но показывает не имена файлов, а их содержимое в виде diff’а (патча).shgit log | head – с помощью этой нехитрой команды можно легко вывести только один экран лога (где-то последние 5 сообщений). Обычно git log листает лог постранично и чтобы выйти из списка нужно нажать клавишу «q».shgit init – создаёт новое пустое хранилище – аналогично
shsvnadmin create path.Работа со списком «assume unchanged» (файлы, не проверяемые на изменения при коммите):
shgit ls-files -v – рекурсивно выводит список всех файлов с префиксами (наподобие
shsvn log), где нижнерегистровая h обозначает, что файл находится в списке assume unchanged.shgit update-index --no-assume-unchanged file.php ... – исключает файл из списка непроверяемых при коммите (--assume-unchanged – наоборот, помечает его таковым).shgit ls-files --others -i --exclude-standard – рекурсивно выводит список всех игнорируемых файлах (через .gitignore и подобные).
Comments