ru/intro edited
[gitmagic/gitmagic.git] / ru / grandmaster.txt
blob300f010f4322af0f7845d3bd1cf1ba6d1c44123b
1 == Гроссмейстерство Git ==
3 Теперь вы уже должны уметь ориентироваться в страницах *git help* и понимать почти всё. Однако точный выбор команды, необходимой для решения конкретной проблемы, может быть утомительным. Возможно, я сберегу вам немного времени: ниже приведены рецепты, пригодившиеся мне в прошлом.
5 === Релизы исходников ===
7 В моих проектах Git управляет в точности теми файлами, которые я собираюсь архивировать и пускать в релиз. Чтобы создать тарбол с исходниками, я выполняю:
9  $ git archive --format=tar --prefix=proj-1.2.3/ HEAD
11 === Сохранение изменений ===
13 В некоторых проектах может быть трудоёмко оповещать Git отдельно о каждом добавлении, удалении и переименовании файла. Вместо этого вы можете выполнить команды
15  $ git add .
16  $ git add -u
18 Git просмотрит файлы в текущем каталоге и сам позаботится о деталях. Вместо второй команды add, выполните *git commit -a*, если вы собираетесь сразу сделать коммит. См. в *git help ignore*, чтобы узнать как указать файлы, которые должны игнорироваться.
20 Вы можете выполнить все это одним махом:
22  $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove
24 Опции *-z* и *-0* предотвращают неверную обработку файловых имен, содержащих специальные символы. Поскольку эта команда добавляет игнорируемые файлы, вы возможно захотите использовать опции -x или -X.
26 === Мой коммит слишком велик ===
28 Вы пренебрегали коммитами слишком долго? Яростно писали код и вспомнили об управлении исходниками только сейчас? Внесли ряд несвязанных изменений, потому что это ваш стиль?
30 Нет поводов для беспокойства. Выполните
32  $ git add -p
34 Для каждой вашей правки Git покажет измененный участок кода и спросит, должно ли это изменение попасть в следующий коммит. Отвечайте «y» или «n». У вас есть идругие варианты, например отложить выбор; введите "?" чтобы узнать больше.
36 Когда закончите, выполните
38  $ git commit
40 для внесения именно тех правок, что вы выбрали («буферизованных» изменений). Убедитесь, что вы не указали опцию *-a*, иначе Git закоммитит все изменения.
42 Что делать, если вы изменили множество файлов во многих местах? Проверка каждого отдельного изменения становится удручающей рутиной. В этом случае используйте *git add -i*. Её интерфейс не так прост, но более гибок. В несколько нажатий кнопок можно добавить или убрать из буфера несколько файлов одновременно, либо просмотреть и выбрать изменения лишь в отдельных файлах. Как вариант, запустите *git commit \--interactive*, который автоматически сделает коммит когда вы закончите.
44 === Индекс — буферная зона Git ===
46 До сих пор мы избегали знаменитого «индекса» Git, но теперь мы должны рассмотреть её, для пояснения вышесказанного. Индекс это временный буфер. Git редко перемещает данные непосредственно между вашим проектом и его историей. Вместо этого Git сначала записывает данные в индекс, а уж затем копирует их из индекса по месту назначения.
48 Например, *commit -a* на самом деле двухэтапный процесс. Сначала слепок текущего состояния каждого из отслеживаемых файлов помещается в индекс. Затем слепок, находящийся в индексе, записывается в историю. Коммит без опции *-a* выполняет только второй шаг, и имеет смысл только после выполнения команд, изменяющих индекс, таких как *git add*.
50 Обычно мы можем не обращать внимания на индекс и делать вид, что взаимодействуем напрямую с историей. Но в данном случае мы хотим более тонкого контроля, поэтому работаем с индексом. Мы помещаем слепок некоторых (но не всех) наших изменений в индекс, после чего окончательно записываем этот аккуратно сформированный слепок.
52 === Не теряй «головы» ===
54 Тег HEAD (англ. «голова», прим. пер.) — как курсор, который обычно указывает на последний коммит, продвигаясь с каждым новым коммитом. Некоторые команды Git позволяют перемещать этот курсор. Например,
56  $ git reset HEAD~3
58 переместит HEAD на три коммита назад. Теперь все команды Git будут работать так, как будто вы не делали последних трех коммитов, хотя файлы останутся в текущем состоянии. В справке описано несколько способов использования этого приёма.
60 Но как вернуться назад в будущее? Ведь предыдущие коммиты о нем ничего не знают.
62 Если у вас есть SHA1 предыдущей «головы», то:
64  $ git reset 1b6d
66 Но допустим, вы никогда его не записывали. Не беспокойтесь: для комнад такого рода Git сохраняет оригинальный HEAD как тег под названием ORIG_HEAD, и вы можете вернуться надёжно и безопасно:
68  $ git reset ORIG_HEAD
70 === Охота за «головами» ===
72 Предположим ORIG_HEAD недостаточно. К примеру, вы только что осознали, что допустили громадную ошибку, и вам нужно вернуться к древнему коммиту в давно забытой ветке.
74 По умолчанию Git хранит коммиты не меньше двух недель, даже если вы приказали уничтожить содержащую их ветку. Проблема в нахождении подходящего хеша. Вы можете просмотреть все хеши в .git/objects и методом проб и ошибок найти нужный. Но есть путь значительно легче.
76 Git записывает все хеши коммитов в .git/logs. В подкатлоге refs содержится полная история активности на всех ветках, а файл HEAD содержит каждое значение хеша, которое когда-либо принимал HEAD. Последнее можно использовать чтобы найти хеши коммитов на случайно обрубленных ветках.
78 Команда reflog предоставляет удобный интерфейс работы с этими логами. Используйте
80  $ git reflog
82 Вместо копирования хешей из reflog, попробуйте
84  $ git checkout "@{10 minutes ago}" # 10 минут назад, прим. пер.
86 Или сделайте чекаут пятого с конца из посещенных коммитов с помощью
88  $ git checkout "@{5}"
90 См. раздел «Specifying Revisions» в *git help rev-parse*, для дополнительной информации.
92 Вы можете захотеть установить более долгий период сохранения удаляемых коммитов. Например,
94  $ git config gc.pruneexpire "30 days"
96 означает, что в удаленные коммиты будут окончательно потеряны только по прошествии 30 дней  и после запуска *git gc*.
98 Также вы можете захотеть отключить автоматический вызов *git gc*:
100  $ git config gc.auto 0
102 В этом случае коммиты будут удаляться только когда вы будете запускать *git gc* вручную.
104 === Git как основа ===
106 Дизайн Git, в истинном духе UNIX, может быть легко использован как низкоуровневый компонент других программ: GUI, веб-интерфейсов, альтернативных интерфейсов командной строки, инструментов управления патчами, импортирования, преобразования, и т.д. Многие команды Git на самом деле - скрипты, стоящие на плечах гигантов. Небольшой доработкой вы можете переделать Git на свой вкус.
108 Простейший трюк — использование алиасов Git для выполнения часто используемых команд:
110  $ git config --global alias.co checkout
111  $ git config --global --get-regexp alias       # отображает текущие алиасы
112  alias.co checkout
113  $ git co foo # то-же, что и «git checkout foo»
115 Также можно выводить текущую ветку в приглашении  командной строки или заголовке окна терминала.
117 Запуск
119  $ git symbolic-ref HEAD
121 выводит название текущей ветки. На практике  вы скорее всего захотите убрать «refs/heads/» и сообщения об ошибках:
123  $ git symbolic-ref HEAD 2> /dev/null | cut -b 12-
125 Подкаталог +contrib+ это целая сокровищница инструментов, построенных на Git. Со временем некоторые из них могут становиться официальными командами. Под Debian и Ubuntu этот каталог находится в +/usr/share/doc/git-core/contrib+.
127 Ещё один из популярных и часто используемых инструментов — workdir/git-new-workdir. Этот скрипт создает с помощью символических ссылок новый рабочий каталог, имеющий общую историю с оригинальным хранилищем:
129  $ git-new-workdir существующее/хранилище новый/каталог
131 Новый каталог и файлы в ней можно воспринимать как клон, с той разницей, что два дерева автоматически остаются синхронизированными ввиду обще истории. Нет необходимости в *merge*, *push* и *pull*.
133 === Рискованные трюки ===
135 Нынешний Git делает случайное уничтожение данных очень сложным.
136 Но если вы знаете, что делаете, вы можете обойти защиту для распространённых команд.
138 *Checkout*: Наличие незакоммиченных изменений прерывает выполнение checkout. Чтобы перейти к нужному коммиту, даже уничтожив свои изменения, используйте «заставляющий» (force, прим. пер.) флаг *-f*: 
140  $ git checkout -f HEAD^
142 С другой стороны, если вы укажете checkot конкретные пути, проверки на безопасность не будет: указанные файлы молча перезаписшутся. Будьте осторожны при использовании checkout таким образом.
144 *Reset*: сброс также прерывается при наличии незакоммиченных изменений. Чтобы заставить его сработать, запустите
146  $ git reset --hard 1b6d
148 *Branch*: Удаление ветки не выполняется, если должно привести к потере изменений. Для принудительного удаления введите
150  $ git branch -D мёртвая_ветка # вместо -d
152 Аналогично, попытка перезаписи ветки путем перемещения будет прервано если может привести к потере данных. Для принудительного перемещений ветки введите
154  $ git branch -M источник цель #вместо -m
156 В отличии от checkout и reset, эти две команды дают отсрочку в удалении данных. Изменения остаются в каталоге .git и могут быть возвращены восстановлениеем нужного хеша из .git/logs (см. выше раздел «Охота за «головами»). По умолчанию они будут храниться по крайней мере две недели.
158 *Clean*: Некоторые команды не будут выполняться, если они могут повредить неотслеживаемые файлы. Если вы уверены, что все неотслеживаемые файлы и каталоги не нужны, то безжалостно удаляйте их командой
160  $ git clean -f -d
162 В следующий раз эта досадная команда выполнится!
164 === Предотвращаем плохие коммиты ===
166 История многих моих проектов полна глупых ошибок. Самое ужасное это проблема недостающих файлов, вызванная забытым *git add*. К счастью, я пока не терял критичных данных при таких случайных пропусках, потому что я редко удаляю оригинальные рабочие каталоги. Обычно я замечаю ошибку несколько коммитов спустя, так что единственный вред это небольшая потеря истории и глупое чувство вины.
168 Также я регулярно совершаю менее серьёзный проступок: завершающие пробелы. Несмотря на безвредность, я не хотел бы, чтобы это появлялось в публичных записях.
170 И наконец я беспокоюсь о неразрешенных конфликтах слияний, хотя пока они не приносили вреда. Обычно я замечаю их при сборке проекта, но иногда могу проглядеть.
172 Если бы я только поставил защиту от дурака, используя _хук_, который бы предупреждал меня об этих проблемах:
174  $ cd .git/hooks
175  $ cp pre-commit.sample pre-commit # В старых версиях Git: chmod +x pre-commit
177 Теперь Git отменит коммит, если обнаружит лишние пробелы или неразрешенные конфликты.
179 Для этого руководства я в конце концов добавил следующее в начало хука *pre-commit*, чтобы защититься от своей рассеянности:
181 if git ls-files -o | grep '\.txt$'; then
182  echo FAIL! Неотслеживаемые .txt файлы.
183  exit 1
186 Несколько операций Git поддерживают хуки, см. *git help hooks*. Вы можете добавить хуки, которые будут сообщать о грамматических ошибках в описаниях коммитов, добавлять новые файлы, расставлять абзацные отступы, прибавлять запись к веб-странице, проигрывать звуки и т.д.
188 Мы использовали пример хука *post-update* раньше, при обсуждении использования Git через http; это заставило Git запускать этот скрипт при каждом перемещении «головы». Пример скрипта *post-update* обновлял несколько файлов, которые нужны Git для связи через не считающиеся с ним средства сообщения, вроде HTTP.