🌍 English ∙ Español ∙ Русский ∙ 繁體中文 ∙ 简体中文 ∙ 한국어 ∙ Tiếng Việt ∙ Français ∙ 日本語
Есть инструкции для астронавтов (а здесь подобное руководство для пользователей Git) о том, как быть, если что-то пошло не так.
Полетные правила - это с большим трудом полученный запас знаний, записанный в форме инструкций, в которых указан последовательный порядок действий в разных ситуациях и сказано, почему нужно делать именно так. По сути это крайне подробный, стандартный план действий в ситуациях, развивающихся по прописанным сценариям. [...]
В НАСА фиксируют все наши промахи, аварии и методы решения проблем с начала 1960 х гг., когда наземные группы проекта Mercury начали составлять сборник "выученных уроков". Теперь в этом сборнике перечислены тысячи сложных ситуаций и их решения – от отказа двигателей, поломки ручек люка до проблем с компьютером.
— Крис Хэдфилд, Руководство астронавта по жизни на Земле (перевод Дмитрия Лазарева).
Для наглядности во всех примерах в этом документе используется измененное приглашение командной строки, чтобы показать текущую ветку и есть ли подготовленные изменения. Ветка заключена в кавычки, а символ *
после ветки означает подготовленные изменения.
Приведенные команды работают на Git версии 2.13.0 и выше. Для обновления Вашей версии Git посетите вебсайт Git.
- Репозитории
- Редактирование коммитов
- Что я только что сохранил?
- Я неправильно написал сообщение коммита
- Я сделал коммит с неправильным именем автора и адресом электронной почты
- Я хочу удалить файл из предыдущего коммита
- Я хочу удалить последний коммит
- Удалить произвольный коммит
- Я пытаюсь опубликовать исправленный коммит, но получаю сообщение об ошибке
- Я случайно сделал жесткий сброс (--hard) и теперь хочу вернуть свои изменения
- Я случайно опубликовал ненужное слияние
- Я случайно закоммитил и опубликовал файлы с конфиденциальными данными
- Я хочу удалить большой файл из истории репозитория
- Мне нужно изменить содержимое коммита, который не является последним
- Подготовка изменений (staging)
- Мне нужно добавить подготовленные изменения в предыдущий коммит
- Я хочу подготовить только часть файла, а не весь файл целиком
- Я хочу добавить изменения одного файла в два разных коммита
- Я подготовил слишком много правок и теперь хочу разделить их на несколько отдельных коммитов
- Я хочу подготовить свои неподготовленные правки и убрать из подготовки то, что уже подготовлено
- Неподготовленные правки
- Я хочу переместить мои неподготовленные правки в новую ветку
- Я хочу переместить неподготовленные правки в другую существующую ветку
- Я хочу отменить мои локальные несохраненные изменения (подготовленные и неподготовленные)
- Я хочу отменить некоторые неподготовленные изменения
- Я хочу отбросить неподготовленные изменения в некоторых файлах
- Я хочу убрать все неподготовленные локальные изменения
- Я хочу удалить все неотслеживаемые файлы
- Я хочу убрать заданный файл из подготовленного
- Ветки
- Я хочу получить список всех веток
- Создать ветку на определенном коммите
- Я стянул изменения (pull) из неправильной ветки или в неправильную ветку
- Я хочу отменить локальные коммиты, чтобы моя ветка стала такой же как на сервере
- Я сохранил коммит в ветку main вместо новой ветки
- Я хочу сохранить файл целиком из другого ref-ish
- Я сделал несколько коммитов в одной ветке, а нужно было сохранять их в разных ветках
- Я хочу удалить локальные ветки, которые были удалены в upstream
- Я нечаянно удалил мою ветку
- Я хочу удалить ветку
- Я хочу удалить несколько веток
- Я хочу переименовать ветку
- Я хочу перейти на удаленную ветку, над которой работает кто-то еще
- Я хочу создать новую удаленную ветку из текущей локальной
- Я хочу настроить локальную ветку на отслеживание удаленной (upstream) ветки
- Я хочу настроить HEAD на отслеживание основной удаленной ветки
- Я сделал изменения в неправильной ветке
- Перебазирование (rebase) и слияние (merge)
- Отложенные изменения (stash)
- Поиск
- Субмодули
- Разное
- Отслеживание файлов
- Я хочу изменить регистр в имени файла, не меняя содержимое файла
- Я хочу переписать локальные файлы при выполнении git pull
- Я хочу удалить файл из git, но оставить сам файл
- Я хочу откатить файл до заданной ревизии
- Я хочу получить список изменений в заданном файле из разных коммитов или веток
- Я хочу, чтобы Git игнорировал изменения в определенном файле
- Отладка с помощью Git
- Конфигурация
- Я не представляю что я сделал неправильно
- Сокращения для команд Git
- Другие ресурсы
Чтобы создать репозиторий Git в текущей папке:
(my-folder) $ git init
Чтобы клонировать (копировать) удаленный репозиторий, скопируйте URL репозитория и запустите:
$ git clone [url]
Git сохранит копию в папке с названием репозитория. Убедитесь, что у Вас есть соединение с удаленным сервером, откуда Вы клонируете (в большинстве случаев это означает наличие выхода в Интернет).
Чтобы клонировать в папку с произвольным именем:
$ git clone [url] name-of-new-folder
Вот несколько возможных проблем:
Если Вы склонировали неправильный репозиторий, то просто удалите появившуюся папку и склонируйте правильный репозиторий.
Если Вы неправильно настроили origin для существующего локального репозиторий, то измените URL-адрес origin на правильный с помощью:
$ git remote set-url origin [правильный url]
См. подробнее на StackOverflow.
Git не разрешает добавлять код в чужой репозиторий без соответствующих прав доступа. Так же, как и GitHub, который, вообще говоря, не является самим Git-ом, а скорее хостингом для репозиториев Git. Тем не менее, вы можете предложить код посредством патчей или, на GitHub, посредством форков (ответвлений) и пулл-реквестов (запросов на слияние).
Сначала немного о форках. Форк - это копия репозитория. Эта операция не имеет прямого отношения к Git, это общая операция на GitHub, Bitbucket, GitLab и на других хостингах репозиториев Git. Вы можете сделать форк с помощью веб-интерфейса.
После создания форка вам нужно склонировать репозиторий на свой компьютер. Вы можете сделать мелкие правки прямо на GitHub без клонирования, но это не "Правила полёта на GitHub", так что рассмотрим, как это сделать у себя на компьютере.
# если вы используете SSH
$ git clone git@github.com:k88hudson/git-flight-rules.git
# если вы используете HTTPS
$ git clone https://github.com/k88hudson/git-flight-rules.git
Если перейти в появившийся каталог и ввести команду git remote
, то напечатается список ссылок на удаленные репозитории. Обычно будет единственная ссылка origin
, которая будет указывать на k88hudson/git-flight-rules
. Ещё вам понадобится ссылка на ваш форк.
Следуя общепринятым соглашениям, название origin
используется для вашего собственного репозитория, а upstream
- для исходного репозитория, с которого вы сделали форк. Так что переименуем origin
в upstream
$ git remote rename origin upstream
Вместо этого можно использовать git remote set-url
, но это дольше и требует больше действий.
Теперь настроим удаленную ссылку на ваш собственный репозиторий.
$ git remote add origin git@github.com:YourName/git-flight-rules.git
Обратите внимание, что у вас два удаленных репозитория.
origin
ссылается на ваш собственный репозиторий.upstream
ссылается на исходный репозиторий.
Репозиторий origin доступен для чтения и записи, тогда как upstream доступен только для чтения.
Когда вы сделаете изменения, которые хотели, отправьте свои изменения (которые обычно делаются в отдельной ветке) в репозиторий origin
. Если вы делаете изменения в отдельной ветке, то удобно пользоваться опцией --set-upstream
, чтобы в будущем при отправке данной локальной ветки не приходилось каждый раз указывать удаленную ветку. Например:
$ (feature/my-feature) git push --set-upstream origin feature/my-feature
Git не располагает командами для создания пулл-реквестов (хотя есть внешние инструменты вроде hub). Так что, когда подготовите необходимые изменения, открывайте GitHub (или другой сервер Git) и создавайте пулл-реквест. Обратите внимание, что сервер автоматически связывает исходный репозиторий и форк.
После этого не забудьте добавить описание предлагаемых изменений.
Со временем репозиторий upstream
может обновиться, и эти обновления нужно внести в ваш репозиторий origin
. Не забывайте, что другие люди тоже вносят свои изменения. Предположим, вы находитесь на ветке для своей фичи и вам нужно её обновить согласно последним изменениям исходного репозитория.
Возможно, вы уже настроили ссылку на исходный удаленный репозиторий. Если нет, тогда сделайте это сейчас. Обычно исходный удаленный репозиторий называют upstream
:
$ (main) git remote add upstream <link-to-original-repository>
# $ (main) git remote add upstream git@github.com:k88hudson/git-flight-rules.git
Теперь вы можете получить последние обновления из upstream.
$ (main) git fetch upstream
$ (main) git merge upstream/main
# или одной командой
$ (main) git pull upstream main
Допустим, Вы не глядя сохранили изменения с помощью git commit -a
и теперь не уверены что именно сохранили. В таком случае Вы можете просмотреть последний коммит в HEAD с помощью:
(main)$ git show
Или
$ git log -n1 -p
Если Вы хотите просмотреть файл из определенного коммита, Вы можете сделать так (<commitid>
- нужный Вам коммит):
$ git show <commitid>:filename
Если Вы неправильно сохранили коммит, но еще не сделали push
, то для исправления сообщения коммита сделайте следующее:
$ git commit --amend --only
Это откроет текстовый редактор по-умолчанию, в котором Вы сможете исправить сообщение. С другой стороны Вы можете сделать это одной командой:
$ git commit --amend --only -m 'xxxxxxx'
Если Вы уже сделали push
, то Вы по-прежнему можете исправить коммит, но после этого придется делать push
с принудительной перезаписью, что не рекомендуется.
Если это один коммит, то исправьте его с помощью amend
$ git commit --amend --no-edit --author "New Authorname <authoremail@mydomain.com>"
Или Вы можете сконфигурировать глобальные настройки автора git config --global author.(name|email)
, а затем выполнить
$ git commit --amend --reset-author --no-edit
Если Вам нужно изменить всю историю, то смотрите документацию для git filter-branch
.
Чтобы удалить изменения файла из предыдущего коммита, сделайте следующее:
$ git checkout HEAD^ myfile
$ git add myfile
$ git commit --amend --no-edit
Когда в коммит был добавлен новый файл и Вы хотите его удалить (только из Git), выполните:
$ git rm --cached myfile
$ git commit --amend --no-edit
Это особенно полезно, когда у Вас открытый патч, а Вы сохранили ненужный файл и теперь нужно сделать принудительный push
для обновления патча в удаленном репозитории. Опция --no-edit
оставляет прежнее сообщение коммита без изменений.
Если хотите удалить опубликованные коммиты, воспользуйтесь приведенным ниже приемом. Однако, это бесповоротно изменит у Вас историю Git, а также испортит историю Git у любого, что уже стянул (pull) изменения из репозитория. Короче говоря, никогда так не делайте, если не уверены.
$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branch]
Если Вы еще не опубликовали коммит, то просто сбросьте ветку в состояние перед Вашим последним коммитом (подготовленные изменения не пропадут):
(my-branch)$ git reset --soft HEAD^
Это работает, если Вы еще не сделали push
. Если Вы уже сделали push
, то единственный по-настоящему безопасный способ это git revert SHAofBadCommit
. Это создаст новый коммит, который отменит все изменения предыдущего коммита. Или, если ветка, в которую вы делаете push
безопасна для перезаписи (т.е. не предполагается, другие разработчики будут стягивать из нее изменения), то просто используйте git push --force-with-lease
. Подробнее см. в этом пункте выше.
Здесь уместны те же предупреждения, что и в пункте выше. По возможности, никогда так не делайте.
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push --force-with-lease [remote] [branch]
Или сделайте интерактивное перебазирование и удалите строки ненужных коммитов.
To https://github.com/yourusername/repo.git
! [rejected] mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Напомним, что подобно перебазированию (см. ниже), исправление коммита (amend) заменяет старый коммит новым, поэтому Вы должны делать принудительный push
(--force-with-lease
) Ваших изменений, если хотите заменить уже опубликованные на удаленном репозитории коммиты. Будьте осторожны, когда так делаете – всегда проверяйте с какой веткой Вы проводите эти действия!
(my-branch)$ git push origin mybranch --force-with-lease
В общем, избегайте делать принудительный push. Лучше создать и опубликовать еще один коммит, чем переписывать измененные коммиты, т.к. это приведет к конфликтам истории у других разработчиков, которые работают с этой или дочерними ветками. --force-with-lease
по-прежнему выдаст ошибку, если кто-то одновременно с Вами работает с данной веткой и Ваш принудительный push переписал бы чужие изменения.
Если Вы абсолютно уверены, что никто кроме Вас не работает с данной веткой или Вы хотите обновить вершину ветви в любом случае, то используйте --force
(-f
), но вообще этого следует избегать.
Если Вы случайно сделали git reset --hard
, то вы можете вернуть назад коммиты, т.к. Git несколько дней хранит все действия в журнале.
Замечание: Это относится только если ваша работа была сохранена, т.е. Вы сделали коммит или stash. git reset --hard
удалит несохраненные изменения, так что пользуйтесь им с осторожностью. (Безопасная опция это git reset --keep
.)
(main)$ git reflog
Вы увидете список Ваших предыдущих коммитов и коммит для сброса. Выберите SHA коммита, который хотите вернуть и снова сделайте сброс:
(main)$ git reset --hard SHA1234
И Вы сможете продолжить работу.
Если Вы случайно сделали слияние в основную ветку разработки до того, как были сделаны все необходимые изменения, Вы по-прежнему можете отменить слияние. Но есть одна загвоздка: Коммит слияния имеет более одного родителя (обычно два).
Команда для решения этой проблемы
(feature-branch)$ git revert -m 1 <commit>
где опция -m 1 говорит выбрать родителя номер 1 (ветка, в которую было сделано слияние) в качестве родителя для отката.
Заметка: номер родителя - это не идентификатор коммита. Допустим, коммит слияния имеет строчку Merge: 8e2ce2d 86ac2e7
. Номер родителя - это порядковый номер (отсчет с 1) нужного родителя в этой строчке, первый идентификатор - номер 1, второй - номер 2 и т.д.
Если Вы случайно опубликовали файлы, содержащие конфиденциальные данные (пароли, ключи и пр.), Вы можете изменить последний коммит с помощью amend. Помните, что как только Вы опубликовали коммит, то Вы должны считать всё его содержание скомпрометированным. С помощью дальнейших шагов можно удалить конфиденциальную информацию из публичного репозиторий или из Вашей локальной копии, вы не сможете удалить свою конфиденциальную информацию у людей, которые могли успеть скопировать себе её. Если Вы закоммитили пароль, то незамедлительно поменяйте его. Если Вы закоммитили ключ, то незамедлительно сгенерируйте новый. Изменения опубликованного коммита недостаточно, потому что кто-нибудь мог скопировать исходный коммит до того, как Вы успели его исправить.
Как только удалите конфиденциальные данные из файла, запустите
(feature-branch)$ git add edited_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
Если Вы хотите удалить файл целиком, но оставить его в локальной копии, то запустите
(feature-branch)$ git rm --cached sensitive_file
echo sensitive_file >> .gitignore
(feature-branch)$ git add .gitignore
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
Другим способом хранения конфиденциальных данных является хранение в переменных окружения.
Если Вы хотите удалить файл целиком и не оставлять его в локальной копии, то запустите
(feature-branch)$ git rm sensitive_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
Если Вы успели сделать другие коммиты после коммита с конфиденциальными данными, то Вам нужно использовать rebase.
Если вы хотите удалить пароль или другую конфиденциальную информацию, то вместо этого смотрите Я случайно закоммитил и опубликовал файлы с конфиденциальными данными.
Даже если вы удалите большие или ненужные файлы из последнего коммита, они останутся в истоии Git, в подкаталоге .git
вашего репозитория и git clone
будет загружать ненужные файлы.
Действия, описанные в этой части руководства требуют принудительной перезаписи истории репозитория Git, так что, если вы сотрудничаете с удаленными разработчиками, что сначала убедитесь, что они опубликовали на сервере все свои локальные изменения.
Есть два способа переписывания истории: встроенная команда git-filter-branch
и сторонняя программа bfg-repo-cleaner
. bfg
значительно понятнее в использовании и быстрее по производительности, но её нужно устанавливать отдельно и она требует Java. Мы опишем оба этих способа. Финальный шаг - это принудительный push ваших изменений, который мы обсудим отдельно, поскольку, в отличие от обычного принудительного push, он приводит к масштабной безвозвратной перезаписи истории.
Использование bfg-repo-cleaner требует наличия Java. Скачайте bfg jar-файл по ссылке здесь. В наших примерах будет использоваться bfg.jar
, а загруженный вами файл будет содержать в названии номер версии, например, bfg-1.13.0.jar
.
Чтобы удалить определенный файл:
(main)$ git rm path/to/filetoremove
(main)$ git commit -m "Commit removing filetoremove"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files filetoremove
Заметьте, что в bfg вы должны использовать имя файла без пути, даже если файл находится в подкаталоге.
Также вы можете пользоваться шаблонами имен:
(main)$ git rm *.jpg
(main)$ git commit -m "Commit removing *.jpg"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files *.jpg
При использовании bfg, файлы, существующие в вашем последнем коммите не будут затронуты. Например, если в вашем репозитории было много больших .tga файлов, а потом вы удалили несколько из них, то bfg не тронет файлы, которые присутствуют в последнем коммите.
Заметьте, что если вы переименовали файл в коммите, например, если он сначала назывался LargeFileFirstName.mp4
и коммит переименовал его в LargeFileSecondName.mp4
, то запуск java -jar ~/Downloads/bfg.jar --delete-files LargeFileSecondName.mp4
не удалит его из истории Git. В таком случае запустите команду --delete-files
с обоими именами или с шаблоном имен.
Команду git-filter-branch
сложнее использовать и она имеет меньше возможностей, но вы можете ей воспользоваться, если не хотите пользоваться bfg
.
В приведенной ниже команде замените filepattern
на требуемое имя или шаблон, например, *.jpg
. Эта команда удалит файлы, подходящие под заданный шаблон из истории всех ветвей.
(main)$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch filepattern' --prune-empty --tag-name-filter cat -- --all
Пояснения:
--tag-name-filter cat
- это неуклюжий, но в то же время самый простой способ применить исходные метки к новым коммитам с помощью команды cat.
--prune-empty
удаляет все коммиты, которые теперь стали пустыми.
Как только вы удалили файлы, которые хотели, внимально проверьте, что ничего не сломали в репозитории - если сломали, то проще всего склонировать ваш репозиторий и начать снова. Для завершения вы можете опционально запустить сборщик мусора Git, чтобы минимизировать размер локальной папки .git folder size, а после этого сделать принудительный push.
(main)$ git reflog expire --expire=now --all && git gc --prune=now --aggressive
(main)$ git push origin --force --tags
После того, как вы переписали всю историю репозитория, операция git push
может оказаться слишком большой и вернет ошибку “The remote end hung up unexpectedly” (Удаленный хост неожиданно прервал соединение)
. Если это произошло, то попробуйте увеличить размер буфера передачи:
(main)$ git config http.postBuffer 524288000
(main)$ git push --force
Если это не работает, то вам придется делать push частями. В команде ниже пробуйте увеличивать <number>
пока push не сработает.
(main)$ git push -u origin HEAD~<number>:refs/head/main --force
После того как только команда push сработала в первый раз, постепенно уменьшайте <number>
, пока не сработает обычная git push
.
Пусть Вы сделали несколько (например, три) коммитов и после этого поняли, что упустили что-то, что по смыслу относится к первому из этих трёх коммитов. Вы могли бы сделать новый коммит, содержащий эти изменения, и тогда у Вас была бы чистая кодовая база, но Ваши коммиты не были бы атомарными (т.е. связанные изменения не содержались бы в едином коммите). В такой ситуации Вы бы хотели изменить коммит, к которому относятся эти изменения, а следующие коммиты оставить как есть. В таком случае Вас может спасти git rebase
.
Рассмотрим ситуацию, когда Вы хотите изменить третий с конца коммит.
(your-branch)$ git rebase -i HEAD~4
запустит интерактивный режим rebase, что позволит Вам отредактировать любые из Ваших последних трёх коммитов. Появится текстовый редактор с подобным содержанием:
pick 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit
которое Вам нужно изменить так:
edit 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit
Это говорит rebase, что Вы хотите изменить третий с конца коммит, а другие два оставить как есть. Теперь сохраните и закройте редактор. После этого Git начнет процедуру rebase. Он остановится на коммите, который Вы пометили для изменений, давая Вам возможность изменить этот коммит. Теперь Вы можете доделать то, что забыли первоначально. Отредактируйте и подготовьте изменения. После этого запустите
(your-branch)$ git commit --amend
это заставит Git пересоздать коммит, но оставить сообщение коммита прежним. После этого тяжелая часть работы завершена.
(your-branch)$ git rebase --continue
сделает оставшуюся работу за Вас.
(my-branch*)$ git commit --amend
Если Вы не хотите менять сообщение коммита, скажите git использовать прежнее сообщение:
(my-branch*)$ git commit --amend -C HEAD
Обычно, если хотите подготовить часть файл, Вы запускаете:
$ git add --patch filename.x
-p
- сокращение для --patch
. Это откроет интерактивный режим. Вы сможете разбить коммит с помощью опции s
, однако, если файл новый, то у Вас не будет такой возможности. При добавлении нового файла делайте так:
$ git add -N filename.x
Затем используйте опцию e
для ручного выбора строк. Запустив git diff --cached
или
git diff --staged
, Вы увидите какие строки вы подготовили по-сравнению с тем, что сохранено в рабочей копии.
git add
добавляет в коммит весь файл целиком. git add -p
позволяет интерактивно выбрать изменения, которые Вы хотите добавить.
git reset -p
откроет интерактивный диалог сброса. Это похоже на git add -p
, за исключением того, что выбор "yes" уберёт правку из готовящегося коммита.
Это сложно. Лучшее, что я смог придумать это отложить (stash) неподготовленные изменения. Затем сделать сброс. После этого вернуть отложенные изменения и добавить их.
$ git stash -k
$ git reset --hard
$ git stash pop
$ git add -A
$ git checkout -b my-branch
$ git stash
$ git checkout my-branch
$ git stash pop
Если Вы хотите отменить все подготовленные и неподготовленные изменения, то можете сделать так:
(my-branch)$ git reset --hard
# or
(main)$ git checkout -f
Это уберет из индекса все подготовленные изменения:
$ git reset
Это удалит все локальные изменения, которые не были сохранены или добавлены в индекс (нужно запускать из корня репозитория):
$ git checkout .
Вы также можете отменить несохраненные изменения в определенном файле или папке:
$ git checkout [some_dir|file.txt]
Еще один способ отменить все несохраненные изменения (длиннее, зато работает в любой папке):
$ git reset --hard HEAD
Это удалит все локальные неотслеживаемые файлы, так что останутся только отслеживаемые:
$ git clean -fd
-x
удалит также и игнорируемые файлы.
Когда Вы хотите избавиться от некоторых, но не всех изменений в Вашей рабочей копии.
Сделайте checkout ненужных изменений, оставив нужные.
$ git checkout -p
# Отвечайте `y` для всех фрагментов, которые Вы хотите выбросить
Другим подходом является использование stash. Отложите все хорошие изменения, сбросьте рабочую копию и верните отложенные хорошие изменения.
$ git stash -p
# Выберите фрагменты, которые Вы хотите сохранить
$ git reset --hard
$ git stash pop
В качестве альтернативы, отложите ненужные изменения, а затем выбросьте их.
$ git stash -p
# Выберите фрагменты, которые Вы не хотите сохранять
$ git stash drop
Когда Вы хотите убрать изменения какого-то файла в Вашей рабочей копии.
$ git checkout myFile
Чтобы убрать изменения в нескольких файлах, перечислите их имена.
$ git checkout myFirstFile mySecondFile
Когда Вы хотите убрать все неподготовленные локальные изменения
$ git checkout .
Когда Вы хотите удалить все неотслеживаемые файлы
$ git clean -f
Иногда один или несколько неотслеживаемых файлов ненароком оказываются в подготовленном. Чтобы убрать их из подготовленного:
$ git reset -- <filename>
Это уберёт файл из подготовленного и он снова будет выглядеть как неотслеживаемый.
Список локальных веток
$ git branch
Список удаленных веток
$ git branch -r
Список всех веток (локальных и удаленных)
$ git branch -a
$ git checkout -b <branch> <SHA1_OF_COMMIT>
Это очередная возможность воспользоваться git reflog
, чтобы посмотреть куда указывала ваша HEAD перед неправильным pull.
(main)$ git reflog
ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward
c5bc55a HEAD@{1}: checkout: checkout message goes here
Просто сбросьте ветку обратно на требуемый коммит:
$ git reset --hard c5bc55a
Готово.
Подтвердите, что не хотите отправлять изменения на сервер.
git status
покажет на сколько коммитов Вы опережаете источник:
(my-branch)$ git status
# On branch my-branch
# Your branch is ahead of 'origin/my-branch' by 2 commits.
# (use "git push" to publish your local commits)
#
Один из способов сбросить до источника (чтобы иметь то же, что и в удаленном репозитории):
(my-branch)$ git reset --hard origin/my-branch
Создайте новую ветку, оставаясь на main:
(main)$ git branch my-branch
Сбросьте ветку main к предыдущему коммиту:
(main)$ git reset --hard HEAD^
HEAD^
- это сокращение для HEAD^1
. Это подставляет первого предка HEAD
, подобно этому HEAD^2
подставляет второго предка коммита (слияния могут иметь 2 предков).
Заметьте, что HEAD^2
это не то же самое, что HEAD~2
(для подробностей см. эту ссылку).
Если не хотите использовать HEAD^
, то найдите хэш коммита, на который Вы хотите установить ветку (git log
может помочь в этом). Затем сделайте сброс к этому хэшу. С помощью git push
удостоверьтесь, что эти изменения отражены в удаленном репозитории.
К примеру, ветка main обязана находится на коммите с хэшем a13b85e
:
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
Перейти на новую ветку для продолжения работы:
(main)$ git checkout my-branch
Скажем, у Вас рабочий spike (см. заметку) на сотни изменений. Всё работает. Теперь Вы сохраняете эту работу в другую ветку:
(solution)$ git add -A && git commit -m "Добавлены все изменения из этого рывка в один большой коммит."
Когда Вы хотите поместить его в ветку (например feature
или develop
), Вы хотите сохранять по целым файлам. А также Вы хотите разбить большой коммит на несколько небольших.
Скажем, Вы имеете:
- ветку
solution
с решением к Вашему spike. На один коммит впередиdevelop
. - ветку
develop
, куда Вы хотите добавить Ваши изменения.
Вы можете выполнить это, перенеся содержимое файла в Вашу ветку:
(develop)$ git checkout solution -- file1.txt
Это скопирует содержимое данного файла из ветки solution
в ветку develop
:
# On branch develop
# Your branch is up-to-date with 'origin/develop'.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: file1.txt
Теперь сделайте коммит как обычно.
Заметка: Spike-решения делаются для анализа или решения проблемы. Эти решения используют, чтобы оценить проблему, и отбрасывают сразу же, как только все получают ясное представление о проблеме. ~ Wikipedia.
Скажем, Вы в ветке main. Запустив git log
, Вы увидите, что сделали два коммита:
(main)$ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee <alexlee@example.com>
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Added CSRF protection
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee <alexlee@example.com>
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Fixed spacing on title
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose <akirose@example.com>
Date: Tue Jul 21 01:12:48 2014 -0400
First commit
Обратим внимание на ссылки на каждый баг (e3851e8
для #21, 5ea5173
для #14).
Во-первых, сбросим ветку main на правильный коммит (a13b85e
):
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
Теперь, мы может создать новую ветку для бага #21:
(main)$ git checkout -b 21
(21)$
Теперь сделаем cherry-pick коммита для бага #21 на верх нашей ветки. Это значит, что мы помещаем этот и только этот коммит напрямую на вершину ветки, какой бы она ни была.
(21)$ git cherry-pick e3851e8
На этом этапе есть вероятность конфликтов. О том как разрешить конфликты см. в главе Здесь были конфликты в разделе интерактивное перебазирование выше.
Теперь давайте создадим новую ветку для бага #14, которая также основана на main
(21)$ git checkout main
(main)$ git checkout -b 14
(14)$
И наконец, сделаем cherry-pick коммита для бага #14:
(14)$ git cherry-pick 5ea5173
Как только Вы слили пулл-реквест на GitHub, Вам предлагают удалить слитую ветку из Вашего форка. Если Вы не планируете продолжать работу в этой ветке, то для поддержания рабочей копии в чистоте Вы можете удалить локальные копии ненужных веток, чтобы не путаться в них.
$ git fetch -p upstream
где upstream
- удаленная ветка, из которой Вы хотите получить изменения.
Если Вы регулярно отправляете изменения в удаленное хранилище, большую часть времени Вы в безопасности. Но в один прекрасный момент Вы всё же можете случайно удалить Ваши ветки. Скажем, мы создали ветку и создали новый файл:
(main)$ git checkout -b my-branch
(my-branch)$ git branch
(my-branch)$ touch foo.txt
(my-branch)$ ls
README.md foo.txt
Давайте добавим его и сохраним.
(my-branch)$ git add .
(my-branch)$ git commit -m 'foo.txt added'
(my-branch)$ foo.txt added
1 files changed, 1 insertions(+)
create mode 100644 foo.txt
(my-branch)$ git log
commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj <siemiatj@example.com>
Date: Wed Jul 30 00:34:10 2014 +0200
foo.txt added
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson <katehudson@example.com>
Date: Tue Jul 29 13:14:46 2014 -0400
Fixes #6: Force pushing after amending commits
Теперь мы переключаемся обратно на main и 'нечаянно' удаляем нашу ветку.
(my-branch)$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
(main)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(main)$ echo О, нет, моя ветка удалена!
О, нет, моя ветка удалена!
На этом этапе Вы должны быть знакомы с 'reflog' (расширенный журнал). Он хранит историю всех действий в репозитории.
(main)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to main
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from main to my-branch
Как мы можем видеть, у нас есть хэш коммита из удаленной ветки. Посмотрим, можем ли мы восстановить удаленную ветку.
(main)$ git checkout -b my-branch-help
Switched to a new branch 'my-branch-help'
(my-branch-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 foo.txt added
(my-branch-help)$ ls
README.md foo.txt
Вуаля! Мы вернули наш удаленный файл обратно. git reflog
также бывает полезен, когда перебазирование срабатывает не так, как Вы хотели.
Чтобы удалить ветку на удаленном репозитории:
(main)$ git push origin --delete my-branch
Вы также можете сделать:
(main)$ git push origin :my-branch
Чтобы удалить локальную ветку:
(main)$ git branch -d my-branch
Чтобы удалить локальную ветку, которая не была слита с отслеживаемой веткой (заданной с помощью --track
или --set-upstream
) или с HEAD:
(main)$ git branch -D my-branch
Скажем, Вы хотите удалить все ветки, начинающиеся с fix/
:
(main)$ git branch | grep 'fix/' | xargs git branch -d
Чтобы переименовать текущую (локальную) ветку:
(main)$ git branch -m new-name
Чтобы переименовать другую (локальную) ветку:
(main)$ git branch -m old-name new-name
Во-первых, получим все ветки из удаленного репозитория:
(main)$ git fetch --all
Скажем, Вы хотите перейти на daves
из удаленного репозитория.
(main)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'
(--track
- это сокращение для git checkout -b [branch] [remotename]/[branch]
)
Это создаст Вам локальную копию ветки daves
и после push
обновления также появятся в удаленном репозитории.
$ git push <remote> HEAD
Если Вы хотите, чтобы текущая локальная ветка отслеживала соответствующую удаленную (upstream) ветку, тогда выполните следующее:
$ git push -u <remote> HEAD
В режиме upstream
или в режиме simple
(по-умолчанию в Git 2.0) параметра push.default
, следующая команда отправит текущую ветку в удаленную ветку, которая была ранее зарегистрирована с помощью -u :
$ git push
Поведение других режимов git push
описано в документации на push.default.
Вы можете настроить текущую локальную ветку на отслеживание удаленной (upstream) ветки используя:
$ git branch --set-upstream-to [remotename]/[branch]
# или для краткости:
$ git branch -u [remotename]/[branch]
Для настройки отслеживаемой удаленной ветки на другую локальную ветку:
$ git branch -u [remotename]/[branch] [local-branch]
При просмотре удаленных веток можно увидеть какую удаленную ветку отслеживает HEAD. Может оказаться, что это не та ветка что нужно.
$ git branch -r
origin/HEAD -> origin/gh-pages
origin/main
Чтобы origin/HEAD
отслеживала origin/main
, выполните команду:
$ git remote set-head origin --auto
origin/HEAD set to main
Вы сделали несохраненные изменения, а потом поняли, что находитесь не в той ветке. Отложите эти изменения, а затем примените их к нужной ветке:
(wrong_branch)$ git stash
(wrong_branch)$ git checkout <correct_branch>
(correct_branch)$ git stash apply
Вы можете слить/перебазировать Вашу ветку с неправильной веткой. А также бывают случаи, когда Вы не можете предугадать успешно ли завершится процесс перебазирования/слияния. Git сохраняет исходный указатель HEAD в переменную ORIG_HEAD перед тем как приступить к опасным операциям, так что вернуть ветку на состояние до перебазирования/слияния просто.
(my-branch)$ git reset --hard ORIG_HEAD
К сожалению, вы должны сделать принудительный push, если хотите, чтобы изменения были отражены на удаленной ветке. Это потому что у вас изменена история. Удаленная ветка не примет изменения, если не сделать принудительный push. Это одна из основных причин, по которым большинство людей основывает свой рабочий процесс на слиянии вместо перебазирования - большие команды могут столкнуться с проблемами, если разработчики будут делать принудительный push
. Используйте это с осторожностью. Безопасный способ использовать перебазирование - это не отражать Ваши изменения напрямую на удаленную ветку, а вместо этого делать следующее:
(main)$ git checkout my-branch
(my-branch)$ git rebase -i main
(my-branch)$ git checkout main
(main)$ git merge --ff-only my-branch
Чтобы узнать больше, см. эту SO ветку.
Предположим, что Вы работаете в ветке, которая стала или станет пулл-реквестом в main
. В простейшем случае когда всё, что Вы хотите сделать - это объединить все коммиты в один единственный коммит и Вам не важны временные метки, Вы можете сделать сброс и заново сделать коммит. Убедитесь, что ветка main обновлена и Ваши изменения сохранены, затем:
(my-branch)$ git reset --soft main
(my-branch)$ git commit -am "New awesome feature"
Если Вы хотите больше контроля, а также сохранить метки времени, Вам нужно сделать кое-что, называемое интерактивным перебазированием:
(my-branch)$ git rebase -i main
Если Вы не работаете с другой веткой, можете делать перебазирование относительно Вашей HEAD
. Например, если Вы хотите объединить последние два коммита, Вам нужно делать перебазирование относительно HEAD~2
. Для последних 3 - HEAD~3
и т.д.
(main)$ git rebase -i HEAD~2
После того, как запустите интерактивное перебазирование, Вы увидите нечто подобное в Вашем текстовом редакторе:
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
pick b729ad5 fixup
pick e3851e8 another fix
# Rebase 8074d12..b729ad5 onto 8074d12
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Все строки, начинающиеся с #
являются комментариями и не оказывают влияния на перебазирование.
Теперь замените команды pick
на команды, перечисленные ниже. Также Вы можете удалить коммиты, удалив соответствующие строки.
Например, если Вы хотите оставить только старейший коммит и объединить все последующие коммиты во второй коммит, Вам нужно рядом с каждым коммитом кроме первого и второго вместо pick
написать f
:
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix
Если Вы хотите объединить эти коммиты и переименовать коммит, Вам нужно дополнительно добавить r
рядом со вторым коммитом или просто используйте s
вместо f
:
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
s b729ad5 fixup
s e3851e8 another fix
Теперь Вы можете переименовать коммит в следующем запросе, который появится.
Newer, awesomer features
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 8074d12
# You are currently editing a commit while rebasing branch 'main' on '8074d12'.
#
# Changes to be committed:
# modified: README.md
#
Если всё успешно, Вы увидите что-то вроде:
(main)$ Successfully rebased and updated refs/heads/main.
--no-commit
производит слияние, но не делает коммит результата, давая пользователю возможность проверить и доработать результат слияния перед коммитом. no-ff
сохраняет доказательства того, что когда-то существовала ветка feature, сохраняя цельность истории проекта.
(main)$ git merge --no-ff --no-commit my-branch
(main)$ git merge --squash my-branch
Иногда у Вас бывает несколько временных коммитов, которые Вы хотите объединить перед публикацией в upstream. Вы не хотите ненароком объединить свои коммиты с уже опубликованными, потому что кто-то уже мог работать с ними.
(main)$ git rebase -i @{u}
Это выполнит интерактивное перебазирование со списком еще не опубликованных коммитов и Вы сможете безопасно упорядочить/исправить/объединить коммиты из списка.
Иногда слияние может создавать проблемы в некоторых файлах. В таких случаях мы можем воспользоваться опцией abort
для прерывания текущего процесса разрешения конфликтов и попробовать вернуться к состоянию перед слиянием.
(my-branch)$ git merge --abort
Эта команда доступна начиная с версии Git >= 1.7.4
Скажем, у меня есть ветка main, ветка feature-1, выросшая из main, и ветка feature-2, выросшая из feature-1. Если я сделаю коммит в feature-1, тогда родительский коммит ветки feature-2 будет не тем, что нужно (это должна быть верхушка feature-1, поскольку мы ответвились от неё). Мы можем исправить это с помощью git rebase --onto
.
(feature-2)$ git rebase --onto feature-1 <первый коммит из feature-2, который Вы не хотите переносить> feature-2
Это помогает в тех случаях, когда у Вас одна функция основывается на другой функции, которая еще не слита, и все исправления feature-1 должны быть отражены в feature-2.
Для проверки того, что все коммиты ветки слиты в другую ветку, Вам нужно сравнить вершины (или любые коммиты) этих ветвей:
(main)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
Это расскажет Вам обо всех коммитах, которые есть в одной, но отсутствуют в другой ветке и выдаст список всех различий ветвей. Или можете сделать так:
(main)$ git log main ^feature/120-on-scroll --no-merges
Если Вы видите это:
noop
Это значит, что Вы пытаетесь сделать перебазирование в ветку, которая уже имеет идентичный коммит или она впереди текущей ветки. Вы может попробовать:
- удостовериться что Ваша ветка main находится там, где она должна быть
- вместо этого перебазировать
HEAD~2
или что-то более раннее
Если Вам не удается успешно завершить перебазирование, то, возможно, Вам придется разрешать конфликты.
Для начала запустите git status
, чтобы увидеть какие файлы содержат конфликты:
(my-branch)$ git status
On branch my-branch
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
both modified: README.md
В данном примере README.md
содержит конфликты. Откройте файл и взгляните на следующее:
<<<<<<< HEAD
some code
=========
some code
>>>>>>> new-commit
Вам нужно выбрать между кодом, который был добавлен в Вашем новом коммите (в данном примере, это все от средней линии до new-commit
) и Вашей HEAD
.
Если Вы хотите сохранить версию из какой-то одной ветки, то используйте --ours
или --theirs
:
(main*)$ git checkout --ours README.md
- Во время слияния используйте
--ours
для сохранения изменений из локальной ветки или--theirs
для сохранения изменений из другой ветки. - Во время перебазирования используйте
--theirs
для сохранения изменений из локальной ветки или--ours
для сохранения изменений из другой ветки. Для объяснения такого обмена см. эту заметку в документации Git.
Если слияние более сложное, можете воспользоваться визуальным редактором различий:
(main*)$ git mergetool -t opendiff
После разрешения всех конфликтов и тестирования кода подготовьте файлы, которые Вы изменили git add
, а затем продолжите перебазирование с помощью git rebase --continue
(my-branch)$ git add README.md
(my-branch)$ git rebase --continue
Если после разрешения всех конфликтов, Вы получили точно такое же дерево, какое было перед коммитом, то вместо этого Вам нужно сделать git rebase --skip
.
В любой момент Вы можете остановить перебазирование и вернуть ветку в начальное состояние, выполнив:
(my-branch)$ git rebase --abort
Чтобы отложить все правки в рабочем каталоге
$ git stash
Если Вы хотите отложить заодно и неотслеживаемые файлы, добавьте опцию -u
.
$ git stash -u
Отложить только один файл из рабочей папки
$ git stash push working-directory-path/filename.ext
Отложить несколько файлов из рабочей папки
$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext
$ git stash save <message>
Во-первых, проверьте список отложенных изменений, используя
$ git stash list
Затем примените заданный stash из списка, используя
$ git stash apply "stash@{n}"
Здесь 'n' показывает позицию stash-а в стеке. Верхний stash имеет позицию 0.
Чтобы найти коммиты с заданной строкой, используйте следующее:
$ git log -S "string to find"
Общие параметры:
-
--source
показывает ссылки, от которых можно добраться до каждого коммита. -
--all
ищет во всех ветках. -
--reverse
выводит коммиты в обратном порядке, это значит, что вверху будет первый коммит, в котором сделано это изменение.
Найти все коммиты по автору или сохранившему изменения:
$ git log --author=<name or email>
$ git log --committer=<name or email>
Не забывайте, что автор и сохранивший изменения - это не всегда один и тот же человек. --author
- это тот, кто написал код, а --committer
- тот, кто сохранил код, написанный автором.
Чтобы найти все коммиты, содержащие заданный файл, Вы можете использовать:
$ git log -- <path to file>
Обычно Вы задаете точный путь, но можете использовать подстановочные знаки:
$ git log -- **/*.js
При использовании подстановочных знаков используйте --name-status
для просмотра списка файлов, сохраненных в каждом коммите:
$ git log --name-status -- **/*.js
Для отслеживания эволюции отдельной функции Вы можете использовать:
$ git log -L :FunctionName:FilePath
Заметьте, что Вы можете добавить и другие опции команды git log
, вроде диапазона ревизий и выборки коммитов.
Чтобы найти все метки для заданного коммита:
$ git tag --contains <commitid>
$ git clone --recursive git://github.com/foo/bar.git
Если уже клонированы:
$ git submodule update --init --recursive
Создание субмодуля довольно прямолинейно, чего не скажешь об удалении. Команды, которые Вам нужны:
$ git submodule deinit submodulename
$ git rm submodulename
$ git rm --cached submodulename
$ rm -rf .git/modules/submodulename
Сначала нужно найти последний коммит, где файл еще существует:
$ git rev-list -n 1 HEAD -- filename
Затем взять версию файла из коммита непосредственно перед удалением:
git checkout deletingcommitid^ -- filename
$ git tag -d <tag_name>
$ git push <remote> :refs/tags/<tag_name>
Если хотите восстановить метку, которая была удалена, Вы можете сделать следующее: во-первых, Вам нужно найти недостижимую метку:
$ git fsck --unreachable | grep tag
Запомните для себя хэш метки. Затем восстановите удаленную метку, используя команду git update-ref
:
$ git update-ref refs/tags/<tag_name> <hash>
Ваша метка была восстановлена.
Если кто-то прислал Вам пулл-реквест на GitHub, но потом удалил свой форк, то вы не сможете клонировать его репозиторий или использовать git am
, поскольку .diff, .patch URL'ы становятся недоступными. Но Вы можете сделать checkout
самого пулл-реквеста используя специальные GitHub's refs. Для получения содержимого PR#1 в новую ветку с названием pr_1:
$ git fetch origin refs/pull/1/head:pr_1
From github.com:foo/bar
* [new ref] refs/pull/1/head -> pr_1
$ git archive --format zip --output /full/path/to/zipfile.zip main
Если в удаленном репозитории есть метка с таким же именем, как и ветка , то после выполнения команды $ git push <remote> <branch>
Вы получите следующую ошибку:
$ git push origin <branch>
error: dst refspec same matches more than one.
error: failed to push some refs to '<git server>'
Исправить это можно, указав Вы хотите опубликовать именно ветку.
$ git push origin refs/heads/<branch-name>
Если Вы хотите опубликовать метку с таким же названием, как у ветки, используйте сходную команду.
$ git push origin refs/tags/<tag-name>
(main)$ git mv --force myfile MyFile
(main)$ git fetch --all
(main)$ git reset --hard origin/main
(main)$ git rm --cached log.txt
Полагая, что хэш желаемого коммита c5f567:
(main)$ git checkout c5f567 -- file1/to/restore file2/to/restore
Если вы хотите откатить файл к состоянию на 1 коммит раньше, чем c5f567, задайте хэш коммита как c5f567~1:
(main)$ git checkout c5f567~1 -- file1/to/restore file2/to/restore
Полагая, что Вы хотите сравнить последний коммит с файлом из коммита c5f567:
$ git diff HEAD:path_to_file/file c5f567:path_to_file/file
# или
$ git diff HEAD c5f567 -- path_to_file/file
Аналогично для веток:
$ git diff main:path_to_file/file staging:path_to_file/file
# или
$ git diff main staging -- path_to_file/file
Это бывает нужно для шаблонов конфигураций или других файлов, предназначенных для добавления учетных записей, которые не нужно коммитить.
$ git update-index --assume-unchanged file-to-ignore
Обратите внимание, что это не удаляет файл из контроля версий - он всего лишь игнорируется локально. Чтобы отменить это и сказать Git снова отслеживать изменения, выполните обратную команду:
$ git update-index --no-assume-unchanged file-to-stop-ignoring
Команда git-bisect с помощью двоичного поиска находит в истории Git коммит, в котором впервые появилась ошибка.
Пусть вы находитесь на ветке main
и хотите найти коммит, которых поломал некую функцию. Вы начинаете процедуру поиска:
$ git bisect start
Затем вы должны указать коммит, где функция поломана (bad), коммит, где функция работает правильно (good). Предположим, что текущая версия поломана, а v1.1.1
- работоспособная:
$ git bisect bad
$ git bisect good v1.1.1
После этого git-bisect
выберет коммит из середины заданного вами диапазона, перейдет на него и спросит у вас: хороший он или плохой. Вы увидите что-то вроде:
$ Bisecting: 5 revision left to test after this (roughly 5 step)
$ [c44abbbee29cb93d8499283101fe7c8d9d97f0fe] Commit message
$ (c44abbb)$
Вы проверите хороший ли коммит. Если хороший:
$ (c44abbb)$ git bisect good
и git-bisect
выберет другой коммит. Этот процесс (выбора good
или bad
) будет повторяться до тех пор, пока диапазон поиска не сузится до единственного коммита, после чего команда выведет описание первого плохого коммита.
В OS X и Linux Ваш файл конфигурации Git хранится в ~/.gitconfig
. В качестве примера я добавил некоторые псевдонимы, которые сам использую для краткости (а также некоторые из моих типичных опечаток), в раздел [alias]
как показано ниже:
[alias]
a = add
amend = commit --amend
c = commit
ca = commit --amend
ci = commit -a
co = checkout
d = diff
dc = diff --changed
ds = diff --staged
extend = commit --amend -C HEAD
f = fetch
loll = log --graph --decorate --pretty=oneline --abbrev-commit
m = merge
one = log --pretty=oneline
outstanding = rebase -i @{u}
reword = commit --amend --only
s = status
unpushed = log @{u}
wc = whatchanged
wip = rebase -i @{u}
zap = fetch -p
day = log --reverse --no-merges --branches=* --date=local --since=midnight --author=\"$(git config --get user.name)\"
delete-merged-branches = "!f() { git checkout --quiet main && git branch --merged | grep --invert-match '\\*' | xargs -n 1 git branch --delete; git checkout --quiet @{-1}; }; f"
Вы не можете этого сделать! Git просто не поддерживает этого, но есть уловка. Вы можете создать файл .gitignore в папке со следующим содержанием:
# Игнорировать всё в этой папке
*
# Кроме этого файла
!.gitignore
Другой общеиспользуемый способ - это создать в папке пустой файл с названием .gitkeep.
$ mkdir mydir
$ touch mydir/.gitkeep
Вы можете назвать его просто .keep , в этом случае вторая строка выше будет touch mydir/.keep
У Вас может быть репозиторий, требующий авторизации. В этом случае вы можете сохранить на время имя пользователя и пароль, и Вам не потребуется вводить их при каждом вызове push или pull. Помощник по учетным записям сделает это за Вас.
$ git config --global credential.helper cache
# Включает кэширование памяти учетных записей
$ git config --global credential.helper 'cache --timeout=3600'
# Задает таймаут для кэша 1 час (задается в секундах)
Найти помощника по учетным записям:
$ git help -a | grep credential
# Покажет доступных помощников по учетным записям
Специфическое кэширование учетных записей для некоторых ОС:
$ git config --global credential.helper osxkeychain
# Для OSX
$ git config --global credential.helper manager
# Git for Windows 2.7.3+
$ git config --global credential.helper gnome-keyring
# Ubuntu и другие дистрибутивы, основанные на GNOME
Вероятно можно найти и других помощников по учетным записям для других дистрибутивов и ОС.
$ git config core.fileMode false
Если Вы хотите задать это поведение по-умолчанию для всех авторизованных пользователей, тогда используйте:
$ git config --global core.fileMode false
Чтобы настроить информацию о пользователе, используемую во всех локальных репозиториях, в частности имя, отображаемое в истории изменений:
$ git config --global user.name “[firstname lastname]”
Чтобы настроить адрес электронной почты, который будет связан с каждой записью в историю:
git config --global user.email “[valid-email]”
Итак, Вы в затруднении - Вы сбросили что-то или Вы слили неправильную ветку, или Вы отправили изменения с принудительной перезаписью и теперь Вы не можете найти свои коммиты. Вы знаете, что в какой-то момент было всё в порядке и Вы хотите вернуться к этому состоянию.
Как раз для этого и нужен git reflog
. reflog
отслеживает все изменения вершины ветки, даже если на эту вершину не указывает ни единая ветвь или метка. В принципе, всякий раз при изменении HEAD, в reflog добавляется новая запись. К сожалению, это работает только с локальными репозиториями и отслеживаются только движения (а не, например, изменения файла, которые не были никуда записаны).
(main)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
reflog выше показывает переход с ветки main на ветку 2.2 и обратно. Затем происходит жесткий сброс на старый коммит. Самое последнее действие находится вверху с надписью HEAD@{0}
.
Если Вы случайно переместитесь назад, то reflog будет содержать коммит (0254ea7), на который указывала ветка main до того, как Вы случайно выбросили 2 коммита.
$ git reset --hard 0254ea7
С помощью git reset
можно вернуть ветку main обратно на коммит, на котором она была прежде. Это обеспечивает безопасность при случайном изменении истории.
(взято из Источник).
Как только Вы освоитесь с командами, описанными выше, возможно, Вам захочется написать сокращения для Git Bash. Это позволит Вас работать гораздо быстрее, выполняя сложные задачи с помощью весьма коротких команд.
alias sq=squash
function squash() {
git rebase -i HEAD~$1
}
Скопируйте эти команды в Ваш .bashrc или .bash_profile.
Если Вы пользуетесь PowerShell на Windows, то и здесь Вы можете настроить псевдонимы и функции. Добавьте эти команды в Ваш профиль, путь к которому указан в переменной $profile
. Узнать больше о профилях можно на портале документации Microsoft About Profiles.
Set-Alias sq Squash-Commits
function Squash-Commits {
git rebase -i HEAD~$1
}
- Learn Enough Git to Be Dangerous - Книга Майкла Хартла, посвященная Git с самых основ
- Pro Git - великолепная книга Скотта Чакона и Бена Страуба про Git
- Git Internals - еще одна великолепная книга Скотта Чакона, посвященная Git
- Nasa handbook
- 19 Git Tips For Everyday Use - Список полезных однострочников Git
- Atlassian's Git tutorial Получите Git сразу с учебниками от начального до продвинутого уровня.
- Изучаем ветвление в Git Интерактивный веб-учебник по ветвлению/слиянию/перебазированию
- Конструктивно о слиянии и перебазировании в Git
- Git Commands and Best Practices Cheat Sheet - Шпаргалка Git с подробными пояснениями
- Git изнутри - Учебник, погружающий во внутренности Git
- git-workflow - Руководство от Aaron Meurer по использованию Git в совместной разработке проектов с открытым исходным кодом
- GitHub как рабочий процесс - Интересный подход к использованию GitHub в качестве рабочего процесса, в частности с пустыми пулл-реквестами
- Githug - Игра для изучения более общих рабочих процессов Git
- learnGitBranching - Изучение ветвлений git в игровой форме.
- firstaidgit.io Выборка наиболее частых вопросов и ответов по Git c поиском
- git-extra-commands - сборник полезных дополнительных скриптов для Git
- git-extras - GIT utilities -- статистика репозитория, REPL, генерация журнала изменений, статистика по авторам изменений и многое другое
- git-fire - git-fire - это плагин для Git, который предупреждает при потенциально опасных действиях, таких как: добавление всех файлов из текущей папки, создание коммита и публикация измений в новую ветку (для предотвращения конфликтов при слиянии).
- git-tips - Краткие советы по Git
- git-town - Общая высокоуровневая поддержка рабочего процесса Git! http://www.git-town.com
- GitKraken - роскошный Git-клиент для Windows, Mac и Linux
- git-cola - еще один Git-клиент для Windows и OS X
- GitUp - новый графический клиент, имеющий весьма своеобразные методы работы со сложностями Git
- gitx-dev - еще один графический Git-клиент для OS X
- Sourcetree - Простота и мощь в красивом и свободном графическом Git-клиенте. Для Windows и Mac.
- Tower - графический Git-клиент для OS X (платный)
- tig - консольный текстовый интерфейс для Git
- Magit - интерфейс для Git, реализованный в виде модуля Emacs.
- GitExtensions - расширение оболочки, плагин для Visual Studio 2010-2015 и автономный инструмент для управления репозиториями Git.
- Fork - быстрый и дружелюбный Git-клиент для Mac (бета)
- gmaster - Git-клиент для Windows с трехсторонним слиянием, обнаружением рефакторинга, семантическим сравнением и слиянием (бета)
- gitk - Git-клиент под Linux для просмотра состояния репозитория.
- SublimeMerge - Шустрый, расширяемый клиент для трехстороннего слияния, мощного поиска и подсветки синтаксиса. В активной разработке.