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 deal

Git deal
Git deal
  1. 1. Создание нового хранилища
    1. 1.1. Set up git
    2. 1.2. Матчасть
    3. 1.3. Next steps
    4. 1.4. Existing Git Repo?
  2. 2. Полезные подкоманды
    1. 2.1. Branch
    2. 2.2. Remote
    3. 2.3. Commit
    4. 2.4. Checkout
    5. 2.5. Stash
    6. 2.6. Другие

Последние несколько дней я основательно познакомился с набирающей обороты новой системой контроля версия – она же 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

...

Команды разделены на три части, пройдёмся по каждой из них.

Set up git

Здесь нужно настроить своё подключение. Для авторизации GitHub использует SSH-ключи (я так понял родной протокол git:// – вообще гибрид или надстройка над SSH). Это требуется сделать только один раз после регистрации, а не каждоый раз после создания нового хранилища.

Как создать свой SSH-ключ описано в статье, ссылка на которую есть в списке команд – здесь у меня не возникло никаких проблем. Замечу только, что всё делается в командной строке bash, устанавливаемой вместе с версией git под Windows.

Две строчки shgit config --global устанавливают настройки по умолчанию для всех подключений git в данной системе.

Матчасть

Теперь начинается самое интересное – но сперва немного матчасти, иначе будет ничего не понятно (мне её пришлось узнавать экспериментально, поэтому не ручаюсь, что всё так, как есть на самом деле).

Перво-наперво, gitMercurial) является распределённой системой контроля версий – как противоположность централизованным системам VCS – SVN и CVS. На практике это значит, что вместо обращения к хранилищу где-то на сервере у вас есть своё локальное хранилище – в полном смысле этого слова: с полной историей коммитов, branch, tags и т.п.

Вся система поддерживается в едином виде с помощью синхронизации локальных хранилищ с каким-то центральным – на сервере (ведь если вы работаете в команде, то у каждого её члена есть точно такая же копия общего хранилища, где хранятся его собственные коммиты – их ведь не видно в вашей, автономной копии). В этом сходство с SVN, но разница в том, что серверное хранилище – точно такое же локальное, как и у вас, тогда как в SVN у вас хранится просто последняя версия изменений, которая обновляется при выполнении shsvn update или shsvn commit.

Как мы видим, серверные хранилища git весьма формально являются «центральными» (то есть едиными) – каждый может её скачать себе в систему и у него будет точно такой же клон. Или форк, если он начнёт вносить туда изменения, но не будет синхронизироваться с сервером.

Матчасть закончилась, но перед продолжением нужно сказать пару слов о терминологии git – у меня первое время из-за непонимания всех этих «pull» и «remote» голова кругом шла. Итак:

clone
Самая базовая операция, она смущала меня долгое время, пока я не понял, что оно обозначает то же, что и checkout в SVN. Оно и понятно – раз нет централизованного хранилища, то «выписка» («checkout») фактически означает создание полной локальной копии хранилища, то есть её клонирование.
pull («тянуть»)
Этим термином обозначаются две вещи: загрузка новых версий с сервера (т.н. «remote») и отправка запроса в основой проект на внесение изменений из вашего форка (fork).
  1. В первом случае аналогом можно считать действие Update из SVN, когда локальная копия синхронизируется с центральным хранилищем – если туда были коммиты, то их содержимое сливается («merge») с локальными правками. В git точно так же – все изменения на сервере сливаются с локальными. Разница, правда, в том, что у одного хранилища может быть сколько угодно «серверов» (remotes) – на то оно и распределённое. Но об этом позже.
  2. Во втором случае речь идёт об ответвлениях (форках, от слова «fork» – вилка). В git каждый может создать свой вариант любого хранилища, просто клонировав его (скачав к себе в систему) – после этого изменения в первичном хранилище не будут сливаться с локальным, если не делать этого вручную. Так вот, «pull» в данном случае – механизм обратной связи, с помощью которого форк может отправлять запросы в первичное хранилище на внесение всех изменений из любой из своих веток. Администраторы первичного хранилища могут, в свою очередь, принять их или отклонить.
push («толкать»)
Действие, обратное pull – означает обновление удалённого хранилища (remote) из локальной ветки (branch). В SVN нечто подобное называется commit, хотя, как я уже упомянал, так как в git нет центрального хранилища, то и «толкать» изменения можно в любые, даже не связанные изначально репозитарии. В SVN же можно обновлять только соотвенствующий центральный репозитарий.
branch («ветка»)
В SVN ветки ничем не отличаются от обычных папок хранилища (как и теги). В git это не так – здесь ветка может быть создана одной командой и фактически это будет ещё один форк (см. pull) хранилища «сам в себе». Если ветка делается в локальном хранилище (то есть не через вёб-интерфейс GitHub), то её не обязательно push-ить на сервер – можете работать с ней, а затем удалить. Переключаться между ветками можно также одной командой – при этом все файлы в папке локального хранилища меняются на свои версии из другой ветки. Фактически, это всё равно, что иметь несколько виртуальных папок хранилищ а одном месте на диске.
remote («удалённый»)
Как уже должно было стать понятным, удалённые (серверные) хранилища ничем не отличаются от локальных, с той только разницей что они доступны из Интернета; точно так же можно иметь несколько локальных хранилищ. Хранилища после «клонированя» не становятся связанными, как в SVN – вот для этого и существует понятие remote, которое обозначает связь между локальным хранилищем и удалённым (или локальным и локальным). Каждая такая связь имеет имя, которое используется в командах типа git push – для обозначения, куда нужно «толкнуть» изменения.

Так, пожалуй теперь с матчастью точно закончено. Приступим к разбору того, как создаётся новое хранилище.

Next steps

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. Первая показывает изменения в текущей структуре файлов и папок по сравнению с последним коммитом, а вторая просто выводит два списка файлов:

  1. Те, что будут включены в коммит (их туда добавляет команда git add, что мы скоро увидим);
  2. Те, что не будут включены в коммит, хотя были изменены – они выводятся красным.

Так вот, перед коммитом нужно сначала добавить файлы в список коммита – в 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:...) у вас будет свой, в остальном всё так же. Что здесь происходит?

  1. git remote add добавляет связь нашего локального хранилища и хранилища на сервере GitHub и присваивает ей имя origin – по этому имени мы потом можем к ней обращаться в разных подкомандах git.
  2. git push «толкает» изменения из нашего хранилища (а точнее, из обозначенной ветки (branch) текущего хранилища) в другое хранилище, имеющее название origin. Это значит, что после успешного завершения команды оба хранилища будут синхронизированы. Последий параметр команды – master – имя ветки, которую нужно «толкнуть». За раз можно «толкать» только одну ветку.
    • параметр -u не обязателен – если он указан, то после одного выполнения команды в полной форме (как она написана в примере) можно будет использовать сокращённую – просто shgit push.

Всё, теперь на сервере GitHub появятся наши изменения и первая ревизия. С этого момента хранилище полностью работоспособно.

Все будущие коммиты делаются точно так же – сначала меняются нужные файлы, затем они добавляются в список коммита командой shgit add * или индивидуально с помощью shgit add dir/file.ext, затем совершается коммит ( shgit commit) и наконец изменения «толкаются» в основное хранилище ( shgit push если вы указали флаг -u, см. выше).

Обратите внимание, что вы можете «толкать» изменения не обязательно после каждого коммита – это уже ваше дело. Они просто не будут видны на сервере, пока вы не выполните git push. А с вашей локальной копией работают все обычные команды – например, git log.

Также заметьте, что после начального выполнения git add remote этого больше делать не нужно, если только вы не перемещаете удалённое хранилище на другой адрес.

Existing Git Repo?

shcd existing_git_repo
git remote add origin git@github.com:ProgerXP/test-repo.git
git push -u origin master

Я не буду снова переписывать написанное чуть выше – фактически, здесь вы делаете то же самое, что и при создании нового репозитария, только пропуская все шаги до добавления новой связи с сервером:

  1. shcd existing_git_repo – перейти в папку со старым хранилищем, которое нужно импортировать в новое хранилище на GitHub;
  2. shgit remote add origin ... – добавить связь между этим хранилищем и адресом («…») и присвоить ей имя origin (можно и любое другое, которое вы затем будете указывать в shgit push);
  3. shgit push -u origin master – отправить изменения из текущего хранилища в хранилище с именем связи «origin» и ветки «master». -u говорит, что нужно использовать эту форму вызова, если в дальнейшем будет вызываться git push (без параметров).

Полезные подкоманды

Здесь я опишу команды, которые мне пригодились, пока я развлекался с созданием нового «pull request». Справку о каждой подкоманде или обо всех командах в целом можно получить по аналогии с svn help – git help [command]. В отличии от svn, справка git открывается в браузере по умолчанию.

Branch

Подкоманда 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 ... – можно «толкнуть» несколько веток одновременно, просто перечислив их в конце команды – работает как на удаление веток, так и на обычное обновление.

Remote

Подкоманда git remote работает со связями локального хранилища с удалёнными. Её формы:

  • shgit remote – выводит список связей – только их имена.
  • shgit remote show NAME – показывает информацию о связи по её имени.
  • shgit remote add NAME git@... – добавляет новую связь с переданным адресом и присваивает ей имя «NAME». После этого команда git push и другие смогут загружать содержимое в хранилище по этому адресу. Заметьте, что хоть подкоманда и называется «remote» хранилище может быть локальным – начинаться с протокола file://. Другие протоколы также допустимы – https://, git:// и т.д.
  • shgit remote rm NAME – удаляет связь.

Commit

Подкоманда 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 изменяет последний коммит вместо создания нового. Полезно для изменения сообщения в логе, хотя может быть использовано и для изменения списка файлов.

Checkout

Подкоманда git checkout выгружает файлы из текущей ревизии хранилища в нужную папку. Пока я пользовался всего одной её формой:

  • shgit checkout -b NEW OLD – создаёт новую ветку, копируя уже существующую, и «переключает» на неё текущую папку (ветка становится активной, что видно в shgit branch).

Stash

Подкоманда 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 RSS20

Your name: Your homepage:

Text & signature markup:You can use UverseWiki markup. In short: **bold**, //italic//, %%code%%, ((URL link)), >inline quote, <[ multiline quote ]>.

Humans! Please enter "J" here: (or turn JavaScript on for automatic verification)
Subscribe by e-mail (manage):
Ctrl+Enter »