Магия Git. Гроссмейстерство Git

Ben Lynn, “Git Magic. Git Grandmastery”, public translation into Russian from English More about this translation.

Translate into another language.

== Гроссмейстерство Git ==

Эта претенциозно названная глава является собранием приемов работы с Git, которые я не смог отнести к другим главам.

=== Релизы исходников ===

В моих проектах Git управляет только теми файлами, которые я собираюсь архивировать и пускать в релиз. Чтобы создать тарбол с исходниками, я выполняю:

$ git archive --format=tar --prefix=proj-1.2.3/ HEAD

=== Зафиксируйте изменения ===

Вручную сообщать 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 add -p

Для каждого внесенного изменения Git покажет измененный участок кода и спросит, должно ли это изменение пройти в следующем коммите. Отвечаем "y" или "n". Если вы хотите сделать что-то другое, например отложить выбор, введите "?" чтобы получить дополнительную информацию.

Как только все будет готово, выполните:

$ git commit

для коммита именно тех изменений, которые вы выбрали ('staged' изменения). Убедитесь, что вы не указали опцию *-a*, в противном случае Git добавит в коммит все изменения.

Что делать, если вы изменили множество файлов во многих местах? Просмотр каждого отдельного изменения - удручающая задача. В этом случае используйте *git add -i*, чей интерфейс менее прост, но более гибок. При помощи нескольких нажатий кнопок можно добавить на этап или убрать с этапа несколько файлов одновременно, либо просмотреть и выделить изменения в отдельных файлах. Как вариант, запустите *git commit \--interactive*, который автоматически сделает коммит после того, как вы закончите.

==== Этапные изменения ====

До сих пор мы избегали такой известной части Git, как 'index', но теперь мы должны разобраться с ней, чтобы пояснить вышесказанное. Индекс представляет собой временную область. Git редко перемещает данные непосредственно между вашим проектом и его историей. Вместо этого, Git сначала записывает данные в индекс, а уж затем копирует данные из индекса по месту назначения.

Например, *commit -a* это на самом деле двухэтапный процесс. Сначала снапшот текущего состояния отслеживаемых файлов помещается в индекс. Затем снапшот, находящийся в индексе, записывается в историю. Коммит без опции *-a* выполняет только второй этап, и имеет смысл только после выполнения

команд, которые изменяют индекс, например *git add*.

Обычно мы можем не обращать внимания на индекс и считать, что взаимодействуем с историей напрямую. Но в такого рода сложных случаях нам нужен усиленный контроль над тем, что записывается в историю, и мы вынуждены работать с индексом. Мы помещаем снапшот только части наших изменений в индекс, а потом записываем этот аккуратно сформированный снапшот.

=== Не теряй HEAD ===

Тег HEAD - как курсор. В нормальном состоянии он указывает на последний коммит, продвигаясь вместе с каждым новым коммитом. Есть команды Git, которые позволяют перемещать этот тег. Например:

$ git reset HEAD~3

переместит HEAD на три коммита назад. Теперь все команды Git будут работать так, как будто вы не делали последних трех коммитов, хотя файлы останутся в текущем состоянии. В справке описаны некоторые методы использования этого эффекта.

Но как вернуться назад в будущее? Ведь предыдущие коммиты о нем ничего не знают.

Если у вас есть SHA1 оригинального HEAD, то:

$ git reset SHA1

Но предположим, что вы никогда его не записывали. Тут тоже беспокоиться не стоит. Для комнад такого рода Git сохраняет оригинальный HEAD как тег под названием ORIG_HEAD, и вы можете вернуться безопасно и без проблем:

$ git reset ORIG_HEAD

=== Охота за 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 как основа ===

Дизайн 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*.

=== Опасные трюки ===

Случайно уничтожить данные очень сложно с сегодняшними версиями Git.

Но если вы знаете, что делаете, вы можете обойти защиту для общих

команд.

*Checkout*: Незакоммиченные изменения не дают сделать чекаут. Чтобы все-таки сделать чекаут нужного коммита, уничтожив свои изменения, используется флаг *-f*:

$ git checkout -f COMMIT

С другой стороны, если вы укажете отдельный путь для чекаута, то проверки на безопасность произведены не будут, и по указанные путям все будет переписываться без каких-либо сообщений. Будьте осторожны, если вы используете чекаут таким образом.

*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

В следующий раз эта надоедливая команда выполнится!

=== Улучшаем свой публичный образ ===

История многих моих проектов полна глупых ошибок. Самое ужасное это кучи недостающих файлов, которые появляются, когда забываешь выполнить *git add*. К счастью, я пока не терял важных файлов из-за того, что пропускал их, потому что я редко удаляю оригинальные рабочие папки. Обычно я замечаю ошибку несколько коммитов спустя, так что единственный вред это отсутствующая история и осознание своей вины.

Также я регулярно совершаю(и коммичу) меньшее зло - завершающие пробелы. Несмотря на безвредность, я не хотел бы, чтобы это появлялось в публичных записях.

И наконец, я беспокоюсь о неразрешенных конфликтах, хотя пока они не приносили вреда. Обычно я замечаю их во время билда, но в некоторых случаях могу проглядеть.

Если бы я только поставил защиту от дурака, используя хук, который бы предупреждал меня об этих проблемах...

Pages: ← previous Ctrl next
1 2

Original (English): Git Magic. Git Grandmastery

Translation: © stroncium, mifistor, eeight, Natamile, Alex .

License: GNU General Public License version 3

translated.by crowd

Like this translation? Share it or bookmark!