Магия Git Магия Git iicollaborators
Скачать 309.22 Kb.
|
Групповая работа в Git Сначала я использовал Git для личного проекта, в котором был единственным разработчиком. Среди команд, связанных с распределенными свойствами Git, мне требовались только pull и clone, чтобы хранить один и тот же проект в разных местах. Позднее я захотел опубликовать свой код при помощи Git и включить изменения помощников. Мне пришлось научиться управлять проектами, которые разрабатываются множеством людей со всего мира. К счастью, это преимущество Git и, возможно, смысл ее существования. 6.1 Кто я? Каждый коммит содержит имя автора и адрес электронной почты, которые отображаются по команде git log. По умолчанию Git использует системные настройки для заполнения этих полей. Чтобы установить их явно, введите: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com Чтобы установить эти параметры только для текущего репозитория, опустите флаг --global. 6.2 Git через SSH, HTTP Предположим, у вас есть SSH доступ к веб-серверу, но Git не установлен. Git может связываться через HTTP, хотя это и менее эффективно, чем его собственный протокол. Скачайте, скомпилируйте, установите Git в вашем аккаунте; создайте репозиторий в директо- рии, доступной через web: $ GIT_DIR=proj.git git init В директории "proj.git" запустите: $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Для старых версий Git команда копирования выдаст ошибку, и вы должны будете запустить: Магия Git 22 / 41 $ chmod a+x hooks/post-update Теперь вы можете публиковать свои последние правки через SSH из любой копии: $ git push web.server:/path/to/proj.git master и кто угодно сможет взять ваш проект через HTTP: $ git clone http://web.server/proj.git 6.3 Git через что угодно Хотите синхронизировать репозитории без серверов или даже без сетевого подключения? Необ- ходимо импровизировать в чрезвычайной ситуации? Мы рассмотрели, как git fast-export и git fast-import могут преобразовать репозитории в один файл и обратно . Мы можем обмениваться такими файлами между репозиториями git с помощью любых носителей, но более эффективным инструментом является git bundle. Отправитель создает пакет (bundle): $ git bundle create somefile HEAD Затем перенесите bundle, somefile, такими средствами, как: email, флешка, дискета, xxd печать и последующее распознавание символов, чтение битов по телефону, дымовые сигналы и т.д. Получатель восстанавливает коммиты из пакета, вводя: $ git pull somefile Получатель может сделать это даже с пустым хранилищем. Несмотря на свой размер, somefile содержит весь исходный Git репозиторий. В больших проектах для уменьшения объема пакет содержит только изменения, которых нет в других репозиториях: $ git bundle create somefile HEAD ^COMMON_SHA1 Если это делается часто, то можно легко забыть, какой коммит был отправлен последним. Справ- ка предлагает для решения этой проблемы использовать метки. А именно, после передачи па- кета введите: $ git tag -f lastbundle HEAD и создавайте пакеты обновления с помощью: $ git bundle create newbundle HEAD ^lastbundle Магия Git 23 / 41 6.4 Патчи: Общее применения Патчи представляют собой текст изменений, который может быть легко понят как человеком, так и компьютером. Это делает его очень привлекательным форматом обмена. Можно послать электронное письмо с патчем для разработчиков, независимо от того, какую систему контроля версий они используют. Пока ваши корреспонденты могут читать электронную почту, они могут увидеть ваши изменения. Аналогичным образом с Вашей стороны все, что Вам требуется, - это адрес электронной почты: нет необходимости в установке онлайн хранилища Git. Напомним, в первой главе: $ git diff COMMIT выводит патч, который может быть вставлен в сообщение электронной почты для обсуждения. В Git репозитории, введите: $ git apply < FILE чтобы применить патч. В более формальной обстановке, когда имя автора и подписи должны быть зарегистрированы, генерируйте соответствующие патчи с определенной точки, набрав: $ git format-patch START_COMMIT Полученные файлы могут быть отправлены с помощью git-send-email или вручную. Вы также можете указать диапазон коммитов: $ git format-patch START_COMMIT..END_COMMIT На принимающей стороне сохраните email в файл и введите: $ git am < FILE Это применит входящие исправления, а также создаст коммит, включающий такую информа- цию, как автор. С web-клиентом электронной почты вам, возможно, потребуется нажать кнопку, чтобы посмот- реть электронную почту в своем первоначальном виде до сохранения исправлений в файл. Есть небольшие различия для Mbox-подобных клиентов электронной почты, но если вы исполь- зуете один из них, то вы, вероятно, тот человек, который может легко настроить его без чтения руководства! 6.5 К сожалению, мы переехали Из предыдущих главах, мы видели, что после клонирования репозитория, набор git push или git pull автоматически проталкивает или стягивает с оригинального URL. Каким образом Git это делает? Секрет кроется в параметрах конфигурации инициализированных при создании клона. Давайте выполним команду: $ git config --list Магия Git 24 / 41 Опция remote.origin.url контролирует исходный URL; "origin" - имя исходного репозитория. Как и имя ветки "master" - это соглашение, мы можем изменить или удалить этот ник, но как правило, нет причин для этого. Если адрес оригинального репозитория изменился, можно обновить его с помощью команды: $ git config remote.origin.url NEW_URL Опция branch.master.merge задает удаленную ветвь по умолчанию для git pull. В ходе первона- чального клонирования, он установлен на текущую ветвь исходного репозитория, так что даже если HEAD исходного репозитария впоследствии переходит на другую ветку, pull будет продол- жать выполняться для исходной ветви. Этот параметр применим только к хранилищу, которое мы впервые клонировали, в его настрой- ках записана branch.master.remote. Если мы выполняем pull из других хранилищ, то мы должны указать необходиму нам ветку: $ git pull ANOTHER_URL master Это объясняет, почему некоторых из наших более ранних примеров push и pull не имели аргу- ментов. 6.6 Удаленные Ветки Когда вы клонируете репозиторий, вы также клонируете все его ветки. Вы можете не заметить это, потому что Git скрывает их: вы должны указать это явно. Это предотвращает пересечение веток в удаленном хранилище с вашими, а также делает Git проще для начинающих. Список удаленных веток можно посмотреть коммандой: $ git branch -r Вы должны увидеть что-то вроде: origin/HEAD origin/master origin/experimental Они представляют собой ветки и HEAD в удаленном хранилище, и могут быть использованы в обычных командах Git. Например, предположим, что вы сделали много коммитов, и хотели бы сравнить с последней загруженной версией. Вы можете найти через журналы для соответству- ющего SHA1 хэш, но это гораздо легче набрать: $ git diff origin/HEAD Также можно увидеть лог ветки "experimental" набрав: $ git log origin/experimental Магия Git 25 / 41 6.7 Несколько Удаленных Веток Предположим, что два других разработчиков работает над нашим проектом, и мы хотим следить за обоими. Мы можем наблюдать более чем за одним хранилище одновременно введя: $ git remote add other ANOTHER_URL $ git pull other some_branch Сейчас мы объединились в ветвь второго хранилища, и мы получили легкий доступ для всех ветвей во всех репозиториях: $ git diff origin/experimental^ other/some_branch5 Но что, если мы просто хотим сравнить их изменения, не затрагивающие нашу собственную работу? Иными словами, мы хотим, чтобы изучить их ветки без изменения нашей рабочей папки. В этом случае вместо pull наберите: $ git fetch # Fetch from origin, the default. $ git fetch other # Fetch from the second programmer. Это выбирает их историю, и ничего больше, так что, хотя рабочий каталог остается нетрону- тыми, мы можем обратиться в любую ветвь в любом хранилище командами Git. Кстати, за ку- лисами, pull просто fetch за которым следует git merge; а последний добавляется в рабочую директорию. Обычно мы используем pull, потому что мы хотим объединить после выполнения fetch; эта ситуация представляет собой заметное исключение. См. git help remote о том, как удалить удаленные хранилища, игнорировать определенные вет- ки и многое другое. 6.8 Мои Настройки Для моих проектов я люблю использовать готовые Git репозитории, в которые я могу сделать pull. Некоторые Git хостинги позволяют создавать собственные ветки проекта нажатием одной кнопки. После того как я выгрузил дерево, я запускаю Git команды для навигации и изучения изменении, которые в идеале хорошо организованны и описаны. Я объединяю мои собственные неопублико- ванные изменения, и, возможно, делаю дальнейшие изменения. После изменения, я выгружаю изменения в официальный репозиторий. Хотя я редко получают взносы, я считаю, этот подход хорошо масштабируется. Смотрите этот пост в блоге Линуса Торвальдса Пребывание в мире Git немного более удобно, чем патч-файлы, как это экономит мне шаги кон- вертации их в коммиты Git. Кроме того, Git автоматически обрабатывает информацию, такую как запись с именем автора и адресом электронной почты, а также время и дату, и просит автора описывать свои собственные изменения. Магия Git 26 / 41 Глава 7 Гроссмейстерство Git Эта претенциозно названная глава является собранием приемов работы с Git, которые я не смог отнести к другим главам. 7.1 Релизы исходников В моих проектах Git управляет только теми файлами, которые я собираюсь архивировать и пус- кать в релиз. Чтобы создать тарбол с исходниками, я выполняю: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD 7.2 Сохранение изменений Вручную сообщать Git о том, что вы добавили, удалили или переименовали файлы, может стать непростой задачей в некоторых проектах. Вместо этого вы можете выполнить команды: $ git add . $ git add -u Git просмотрит файлы в текущем каталоге и обработает изменения сам. Вместо второй команды add, выполните git commit -a, если вы хотите также сделать коммит с изменениями. В git help ignore можно посмотреть, как указать, какие файлы должны игнорироваться. Вы можете выполнить все это в один прием: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove Опции -z и -0 предотвращают побочные эффекты от файловых имен, содержащих специальные символы. Поскольку эта команда добавляет игнорируемые файлы, вы можете использовать оп- ции -x или -X. Магия Git 27 / 41 7.3 Слишком большой коммит Вы пренебрегали коммитами слишком долго? Яростно писали код и вспомнили о контроле ис- ходников только сейчас? Внесли ряд несвязанных изменений, потому что это ваш стиль? Никаких проблем. Выполните: $ git add -p Для каждого внесенного изменения Git покажет измененный участок кода и спросит, должно ли это изменение пройти в следующем коммите. Отвечаем "y" или "n". Если вы хотите сделать что- то другое, например отложить выбор, введите "?" чтобы получить дополнительную информацию. Как только все будет готово, выполните: $ git commit для коммита именно тех изменений, которые вы выбрали (staged изменения). Убедитесь, что вы не указали опцию -a, в противном случае Git добавит в коммит все изменения. Что делать, если вы изменили множество файлов во многих местах? Просмотр каждого отдель- ного изменения - удручающая задача. В этом случае используйте git add -i, чей интерфейс менее прост, но более гибок. При помощи нескольких нажатий кнопок можно добавить на этап или убрать с этапа несколько файлов одновременно, либо просмотреть и выделить изменения в отдельных файлах. Как вариант, запустите git commit --interactive, который автоматически сделает коммит после того, как вы закончите. 7.3.1 Этапные изменения До сих пор мы избегали такой известной части Git, как index, но теперь мы должны разобрать- ся с ней, чтобы пояснить вышесказанное. Индекс представляет собой временную область. Git редко перемещает данные непосредственно между вашим проектом и его историей. Вместо это- го, Git сначала записывает данные в индекс, а уж затем копирует данные из индекса по месту назначения. Например, commit -a это на самом деле двухэтапный процесс. Сначала снапшот текущего со- стояния отслеживаемых файлов помещается в индекс. Затем снапшот, находящийся в индексе, записывается в историю. Коммит без опции -a выполняет только второй этап, и имеет смысл только после выполнения команд, которые изменяют индекс, например git add. Обычно мы можем не обращать внимания на индекс и считать, что взаимодействуем с историей напрямую. Но в такого рода сложных случаях нам нужен усиленный контроль над тем, что запи- сывается в историю, и мы вынуждены работать с индексом. Мы помещаем снапшот только части наших изменений в индекс, а потом записываем этот аккуратно сформированный снапшот. 7.4 Не теряй HEAD Тег HEAD - как курсор. В нормальном состоянии он указывает на последний коммит, продвигаясь вместе с каждым новым коммитом. Есть команды Git, которые позволяют перемещать этот тег. Например: $ git reset HEAD3 Магия Git 28 / 41 переместит HEAD на три коммита назад. Теперь все команды Git будут работать так, как будто вы не делали последних трех коммитов, хотя файлы останутся в текущем состоянии. В справке описаны некоторые методы использования этого эффекта. Но как вернуться назад в будущее? Ведь предыдущие коммиты о нем ничего не знают. Если у вас есть SHA1 оригинального HEAD, то: $ git reset SHA1 Но предположим, что вы никогда его не записывали. Тут тоже беспокоиться не стоит. Для ком- над такого рода Git сохраняет оригинальный HEAD как тег под названием ORIG_HEAD, и вы можете вернуться безопасно и без проблем: $ git reset ORIG_HEAD 7.5 Охота за HEAD'ами Предположим ORIG_HEAD недостаточно. Предположим, что вы только что осознали, что допу- стили громадную ошибку, и вам нужно вернуться в очень старый коммит давно забытой ветки. По умолчанию Git хранит коммиты по крайней мере в течении двух недель, даже если вы сказали ему уничтожить ветку с ними. Проблема в нахождении подходящего хеша. Вы можете просмотреть хеши в .git/objects и методом проб и ошибок найти нужный. Но есть путь значительно легче. Git записывает все хеши коммитов в .git/logs. В папке refs содержится история активности на всех ветках, а файл HEAD содержит каждое значение хеша, которое когда-либо принимал HEAD. Второе можно использовать чтобы найти хеши коммитов на случайно обрубленных ветках. Команда reflog предоставляет удобный интерфейс работы с этими логами. Используйте: $ git reflog Вместа копипейста хешей из reflog, попробуйте: $ git checkout "@{10 minutes ago}" Или сделайте чекаут пятого из последних посещенных коммитов с помощью: $ git checkout "@{5}" Смотрите секцию "Specifying Revisions" git help rev-parse, если вам нужна дополнительная ин- формация. Вам может потребоваться установить более долгий период сохранения удаляемых коммитов. Например, выполнение: $ git config gc.pruneexpire "30 days" означает, что в удаленные коммиты будут окончательно потеряны только после того, как прой- дут 30 дней с момента удаления, и будет запущена git gc. Также вам может потребоваться отключить автоматическое выполнение git gc: $ git config gc.auto 0 После этого коммиты будут удаляться только когда вы будете запускать git gc самостоятельно. Магия Git 29 / 41 7.6 Git как основа Дизайн Git’a, разработанный в UNIX стиле, позволяет использовать Git как низкоуровневый ком- понент других программ: GUI, веб-интерфейсов, альтернативных командных строк, инструмен- тов управления патчами, импортирования, преобразования, и т.д. На самом деле, многие ко- манды Git - сами по себе скрипты, стоящие на плечах гигантов. Немного поигравшись с Git, вы можете заставить его удовлетворять многие ваши потребности. Простейший трюк - использование алиасов Git для выполнения часто используемых команд: $ git config --global alias.co checkout $ git config --global --get-regexp alias # отображает текущие алиасы alias.co checkout $ git co foo # то-же, что 'git checkout foo' Также можно выводить текущую ветку в командную строку или название окна терминала. Запуск $ git symbolic-ref HEAD выводит название текущей ветки. Для практического использования вы скорее всего захотите убрать "refs/heads/" и сообщения об ошибках: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- Папка contrib это целая сокровищница инструментов, построенных на Git. Со временем неко- торые из них могут становиться официальными командами. Под Debian и Ubuntu эта папка на- ходится в /usr/share/doc/git-core/contrib. workdir/git-new-workdir - один из популярных и часто используемых инструментов. С помо- щью хитрых симлинков этот скрипт создает новую рабочую папку, которая будет иметь общую историю с оригинальным репозиторием: $ git-new-workdir an/existing/repo new/directory Можно думать о новой папке и о файлах в ней, как о клоне, за исключением того, что так как история общая, два дерева автоматически синхронизуются. Нет необходимости в merge, push и pull. 7.7 Опасные трюки Случайно уничтожить данные очень сложно с сегодняшними версиями Git. Но если вы знаете, что делаете, вы можете обойти защиту для общих команд. Checkout: Незакоммиченные изменения не дают сделать чекаут. Чтобы все-таки сделать чекаут нужного коммита, уничтожив свои изменения, используется флаг -f: $ git checkout -f COMMIT Магия Git 30 / 41 С другой стороны, если вы укажете отдельный путь для чекаута, то проверки на безопасность произведены не будут, и по указанные путям все будет переписываться без каких-либо сообще- ний. Будьте осторожны, если вы используете чекаут таким образом. Reset: Ресет также нельзя провести, если есть незакоммиченные изменения. Чтобы обойти это, запустите: $ git reset --hard [COMMIT] Branch: Удаление ветки не проходит, если приводит к потере изменений. Для принудительного удаления используйте: $ git branch -D BRANCH # вместо -d Аналогично, попытка перезаписи ветки путем перемещения не пройдет, если какие-то данные будут потеряны. Чтобы принудительно переместить ветку, введите: $ git branch -M [SOURCE] TARGET #вместо -m В отличии от чекаута и ресета, эти две команды задерживают удаление данных. Изменения оста- ются в папке .git и могут быть восстановлены с помощью нужного хеша из .git/logs(смотрите параграф "Охота за HEAD'ами" выше). По умолчанию они будут храниться по крайней мере две недели. Clean: Некоторые команды не будут выполняться, если они могут повредить неотслеживаемые файлы. Если вы уверены, что все неотслеживаемые файлы и папки вам не нужны, то безжалост- но удаляйте их командой: $ git clean -f -d В следующий раз эта надоедливая команда выполнится! 7.8 Улучшаем свой публичный образ История многих моих проектов полна глупых ошибок. Самое ужасное это кучи недостающих файлов, которые появляются, когда забываешь выполнить git add. К счастью, я пока не терял важных файлов из-за того, что пропускал их, потому что я редко удаляю оригинальные рабочие папки. Обычно я замечаю ошибку несколько коммитов спустя, так что единственный вред это отсутствующая история и осознание своей вины. Также я регулярно совершаю(и коммичу) меньшее зло - завершающие пробелы. Несмотря на безвредность, я не хотел бы, чтобы это появлялось в публичных записях. И наконец, я беспокоюсь о неразрешенных конфликтах, хотя пока они не приносили вреда. Обычно я замечаю их во время билда, но в некоторых случаях могу проглядеть. Если бы я только поставил защиту от дурака, используя хук, который бы предупреждал меня об этих проблемах… $ cd .git/hooks $ cp pre-commit.sample pre-commit # В старых версиях Git: chmod +x pre-commit Теперь Git отменяет коммит, если обнаружены ненужные пробелы или неразрешенные конфлик- ты. Для этого руководства я в конце концов добавил следующее в начало pre-commit хука, чтобы защититься от своей рассеянности: Магия Git 31 / 41 if git ls-files -o | grep \.txt$; then echo FAIL! Неотслеживаемые .txt файлы. exit 1 fi Несколько операций Git поддерживают хуки, смотрите git help hooks. Вы можете добавить хуки, которые будут сообщать о грамматических ошибках в комментариях к коммиту, добавлять новые файлы, делать отступы перед параграфами, добавлять записи на веб-страничку, проигрывать звуки, в общем, делать что угодно… Мы встречались с post-update хуком раньше, когда обсуждали Git через http. Этот хук обновлял несколько файлов, которые необходимы для ненативнох видов коммуникации с Git. |