Есть инструкции для астронавтов (а здесь подобное руководство для пользователей Git) о том, как быть, если что-то пошло не так.
Полетные правила - это с большим трудом полученный запас знаний, записанный в форме инструкций, в которых указан последовательный порядок действий в разных ситуациях и сказано, почему нужно делать именно так. По сути это крайне подробный, стандартный план действий в ситуациях, развивающихся по прописанным сценариям. [...]
В НАСА фиксируют все наши промахи, аварии и методы решения проблем с начала 1960 х гг., когда наземные группы проекта Mercury начали составлять сборник "выученных уроков". Теперь в этом сборнике перечислены тысячи сложных ситуаций и их решения – от отказа двигателей, поломки ручек люка до проблем с компьютером.
— Крис Хэдфилд, Руководство астронавта по жизни на Земле (перевод Дмитрия Лазарева).
Для наглядности во всех примерах в этом документе используется измененное приглашение командной строки, чтобы показать текущую ветку и есть ли подготовленные изменения. Ветка заключена в кавычки, а символ *
после ветки означает подготовленные изменения.
Содержание generated with DocToc
- Редактирование коммитов
- Что я только что сохранил?
- Я неправильно написал сообщение коммита
- Я сделал коммит с неправильным именем автора и адресом электронной почты
- Я хочу удалить файл из предыдущего коммита
- Я хочу удалить последний коммит
- Удалить произвольный коммит
- Я пытаюсь опубликовать исправленный коммит, но получаю сообщение об ошибке
- Я случайно сделал жесткий сброс (--hard) и теперь хочу вернуть свои изменения
- Подготовка изменений (staging)
- Неподготовленные правки
- Я хочу переместить мои неподготовленные правки в новую ветку
- Я хочу переместить неподготовленные правки в другую существующую ветку
- Я хочу отменить мои локальные несохраненные изменения (подготовленные и неподготовленные)
- Я хочу отменить некоторые неподготовленные изменения
- Я хочу отбросить неподготовленные изменения в некоторых файлах
- Я хочу убрать все неподготовленные локальные изменения
- Я хочу удалить все неотслеживаемые файлы
- Ветки
- Я хочу получить список всех веток
- Создать ветку на определенном коммите
- Я стянул изменения (pull) из неправильной ветки или в неправильную ветку
- Я хочу отменить локальные коммиты, чтобы моя ветка стала такой же как на сервере
- Я сохранил коммит в ветку master вместо новой ветки
- Я хочу сохранить файл целиком из другого ref-ish
- Я сделал несколько коммитов в одной ветке, а нужно было сохранять их в разных ветках
- Я хочу удалить локальные ветки, которые были удалены в upstream
- Я нечаянно удалил мою ветку
- Я хочу удалить ветку
- Я хочу переименовать ветку
- Я хочу перейти на удаленную ветку, над которой работает кто-то еще
- Я хочу создать новую удаленную ветку из текущей локальной
- Я хочу настроить локальную ветку на отслеживание удаленной (upstream) ветки
- Я хочу настроить HEAD на отслеживание основной удаленной ветки
- Перебазирование (rebase) и слияние (merge)
- Отложенные изменения (stash)
- Поиск
- Субмодули
- Разное
- Отслеживание файлов
- Конфигурация
- Я не представляю что я сделал неправильно
- Другие ресурсы
Допустим, Вы не глядя сохранили изменения с помощью git commit -a
и теперь не уверены что именно сохранили. В таком случае Вы можете просмотреть последний коммит в HEAD с помощью:
(master)$ git show
Или
$ git log -n1 -p
Если Вы хотите просмотреть файл из определенного коммита, Вы можете сделать так (<commitid>
- нужный Вам коммит):
$ git show <commitid>:filename
Если Вы неправильно сохранили коммит, но еще не сделали push
, то для исправления сообщения коммита сделайте следующее:
$ git commit --amend
Это откроет текстовый редактор по-умолчанию, в котором Вы сможете исправить сообщение. С другой стороны Вы можете сделать это одной командой:
$ git commit --amend -m 'xxxxxxx'
Если Вы уже сделали push
, то Вы по-прежнему можете исправить коммит, но после этого придется делать push
с принудительной перезаписью, что не рекомендуется.
Если это один коммит, то исправьте его с помощью amend
$ git commit --amend --no-edit --author "New Authorname <[email protected]>"
Или Вы можете сконфигурировать глобальные настройки автора 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@{1}
Это работает, если Вы еще не сделали 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
.)
(master)$ git reflog
Вы увидете список Ваших предыдущих коммитов и коммит для сброса. Выберите SHA коммита, который хотите вернуть и снова сделайте сброс:
(master)$ git reset --hard SHA1234
И Вы сможете продолжить работу.
(my-branch*)$ git commit --amend
Обычно, если хотите подготовить часть файл, Вы запускаете:
$ 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
позволяет интерактивно выбрать изменения, которые Вы хотите добавить.
Это сложно. Лучшее, что я смог придумать это отложить (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
(master)$ 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 branch
Список удаленных веток
$ git branch -r
Список всех веток (локальных и удаленных)
$ git branch -a
$ git checkout -b <branch> <SHA1_OF_COMMIT>
Это очередная возможность воспользоваться git reflog
, чтобы посмотреть куда указывала ваша HEAD перед неправильным pull.
(master)$ 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)
#
Один из способов сбросить до источника (чтобы иметь то же, что и в удаленном репозитории):
(master)$ git reset --hard origin/my-branch
Создайте новую ветку, оставаясь на master:
(master)$ git branch my-branch
Сбросьте ветку master к предыдущему коммиту:
(master)$ git reset --hard HEAD^
HEAD^
- это сокращение для HEAD^1
. Это подставляет первого предка HEAD
, подобно этому HEAD^2
подставляет второго предка коммита (слияния могут иметь 2 предков).
Заметьте, что HEAD^2
это не то же самое, что HEAD~2
(для подробностей см. эту ссылку).
Если не хотите использовать HEAD^
, то найдите хэш коммита, на который Вы хотите установить ветку (git log
может помочь в этом). Затем сделайте сброс к этому хэшу. С помощью git push
удостоверьтесь, что эти изменения отражены в удаленном репозитории.
К примеру, ветка master обязана находится на коммите с хэшем a13b85e
:
(master)$ git reset --hard a13b85e
HEAD is now at a13b85e
Перейти на новую ветку для продолжения работы:
(master)$ 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.
Скажем, Вы в ветке master. Запустив git log
, Вы увидите, что сделали два коммита:
(master)$ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee <[email protected]>
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Added CSRF protection
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee <[email protected]>
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Fixed spacing on title
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose <[email protected]>
Date: Tue Jul 21 01:12:48 2014 -0400
First commit
Обратим внимание на ссылки на каждый баг (e3851e8
для #21, 5ea5173
для #14).
Во-первых, сбросим ветку master на правильный коммит (a13b85e
):
(master)$ git reset --hard a13b85e
HEAD is now at a13b85e
Теперь, мы может создать новую ветку для бага #21:
(master)$ git checkout -b 21
(21)$
Теперь сделаем cherry-pick коммита для бага #21 на верх нашей ветки. Это значит, что мы помещаем этот и только этот коммит напрямую на вершину ветки, какой бы она ни была.
(21)$ git cherry-pick e3851e8
На этом этапе есть вероятность конфликтов. О том как разрешить конфликты см. в главе Здесь были конфликты в разделе интерактивное перебазирование выше.
Теперь давайте создадим новую ветку для бага #14, которая также основана на master
(21)$ git checkout master
(master)$ git checkout -b 14
(14)$
И наконец, сделаем cherry-pick коммита для бага #14:
(14)$ git cherry-pick 5ea5173
Как только Вы слили пулл-реквест на GitHub, Вам предлагают удалить слитую ветку из Вашего форка. Если Вы не планируете продолжать работу в этой ветке, то для поддержания рабочей копии в чистоте Вы можете удалить локальные копии ненужных веток, чтобы не путаться в них.
$ git fetch -p upstream
где upstream
- удаленная ветка, из которой Вы хотите получить изменения.
Если Вы регулярно отправляете изменения в удаленное хранилище, большую часть времени Вы в безопасности. Но в один прекрасный момент Вы всё же можете случайно удалить Ваши ветки. Скажем, мы создали ветку и создали новый файл:
(master)$ 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 <[email protected]>
Date: Wed Jul 30 00:34:10 2014 +0200
foo.txt added
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson <[email protected]>
Date: Tue Jul 29 13:14:46 2014 -0400
Fixes #6: Force pushing after amending commits
Теперь мы переключаемся обратно на master и 'нечаянно' удаляем нашу ветку.
(my-branch)$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
(master)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(master)$ echo О, нет, моя ветка удалена!
О, нет, моя ветка удалена!
На этом этапе Вы должны быть знакомы с 'reflog' (расширенный журнал). Он хранит историю всех действий в репозитории.
(master)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to master
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from master to my-branch
Как мы можем видеть, у нас есть хэш коммита из удаленной ветки. Посмотрим, можем ли мы восстановить удаленную ветку.
(master)$ 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
также бывает полезен, когда перебазирование срабатывает не так, как Вы хотели.
Чтобы удалить ветку на удаленном репозитории:
(master)$ git push origin --delete my-branch
Вы также можете сделать:
(master)$ git push origin :my-branch
Чтобы удалить локальную ветку:
(master)$ git branch -d my-branch
Чтобы удалить локальную ветку, которая не была слита с отслеживаемой веткой (заданной с помощью --track
или --set-upstream
) или с HEAD:
(master)$ git branch -D my-branch
Чтобы переименовать текущую (локальную) ветку:
(master)$ git branch -m new-name
Чтобы переименовать другую (локальную) ветку:
(master)$ git branch -m old-name new-name
Во-первых, получим все ветки из удаленного репозитория:
(master)$ git fetch --all
Скажем, Вы хотите перейти на daves
из удаленного репозитория.
(master)$ 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>
Если Вы хотите, чтобы текущая локальная ветка отслеживала соответствующую удаленную (upstream) ветку, тогда выполните следующее:
$ git push -u <remote>
В режиме 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 -rr
origin/HEAD -> origin/gh-pages
origin/master
Чтобы origin/HEAD
отслеживала origin/master
, выполните команду:
$ git remote set-head origin --auto
origin/HEAD set to master
Вы можете слить/перебазировать Вашу ветку с неправильной веткой. А также бывают случаи, когда Вы не можете предугадать успешно ли завершится процесс перебазирования/слияния. Git сохраняет исходный указатель HEAD в переменную ORIG_HEAD перед тем как приступить к опасным операциям, так что вернуть ветку на состояние до перебазирования/слияния просто.
(my-branch)$ git reset --hard ORIG_HEAD
К сожалению, вы должны сделать принудительный push, если хотите, чтобы изменения были отражены на удаленной ветке. Это потому что у вас изменена история. Удаленная ветка не примет изменения, если не сделать принудительный push. Это одна из основных причин, по которым большинство людей основывает свой рабочий процесс на слиянии вместо перебазирования - большие команды могут столкнуться с проблемами, если разработчики будут делать принудительный push
. Используйте это с осторожностью. Безопасный способ использовать перебазирование - это не отражать Ваши изменения напрямую на удаленную ветку, а вместо этого делать следующее:
(master)$ git checkout my-branch
(my-branch)$ git rebase -i master
(my-branch)$ git checkout master
(master)$ git merge --ff-only my-branch
Чтобы узнать больше, см. эту SO ветку.
Предположим, что Вы работаете в ветке, которая стала или станет пулл-реквестом в master
. В простейшем случае когда всё, что Вы хотите сделать - это объединить все коммиты в один единственный коммит и Вам не важны временные метки, Вы можете сделать сброс и заново сделать коммит. Убедитесь, что ветка master обновлена и Ваши изменения сохранены, затем:
(my-branch)$ git reset --soft master
(my-branch)$ git commit -am "New awesome feature"
Если Вы хотите больше контроля, а также сохранить метки времени, Вам нужно сделать кое-что, называемое интерактивным перебазированием:
(my-branch)$ git rebase -i master
Если Вы не работаете с другой веткой, можете делать перебазирование относительно Вашей HEAD
. Например, если Вы хотите объединить последние два коммита, Вам нужно делать перебазирование относительно HEAD~2
. Для последних 3 - HEAD~3
и т.д.
(master)$ 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 'master' on '8074d12'.
#
# Changes to be committed:
# modified: README.md
#
Если всё успешно, Вы увидите что-то вроде:
(master)$ Successfully rebased and updated refs/heads/master.
--no-commit
производит слияние, но не делает коммит результата, давая пользователю возможность проверить и доработать результат слияния перед коммитом. no-ff
сохраняет доказательства того, что когда-то существовала ветка feature, сохраняя цельность истории проекта.
(master)$ git merge --no-ff --no-commit my-branch
(master)$ git merge --squash my-branch
Иногда у Вас бывает несколько временных коммитов, которые Вы хотите объединить перед публикацией в upstream. Вы не хотите ненароком объединить свои коммиты с уже опубликованными, потому что кто-то уже мог работать с ними.
(master)$ git rebase -i @{u}
Это выполнит интерактивное перебазирование со списком еще не опубликованных коммитов и Вы сможете безопасно упорядочить/исправить/объединить коммиты из списка.
Иногда слияние может создавать проблемы в некоторых файлах. В таких случаях мы можем воспользоваться опцией abort
для прерывания текущего процесса разрешения конфликтов и попробовать вернуться к состоянию перед слиянием.
(my-branch)$ git merge --abort
Эта команда доступна начиная с версии Git >= 1.7.4
Для проверки того, что все коммиты ветки слиты в другую ветку, Вам нужно сравнить вершины (или любые коммиты) этих ветвей:
(master)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
Это расскажет Вам обо всех коммитах, которые есть в одной, но отсутствуют в другой ветке и выдаст список всех различий ветвей. Или можете сделать так:
(master)$ git log master ^feature/120-on-scroll --no-merges
Если Вы видите это:
noop
Это значит, что Вы пытаетесь сделать перебазирование в ветку, которая уже имеет идентичный коммит или она впереди текущей ветки. Вы может попробовать:
- удостовериться что Ваша ветка master находится там, где она должна быть
- вместо этого перебазировать
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
:
(master*)$ git checkout --ours README.md
- Во время слияния используйте
--ours
для сохранения изменений из локальной ветки или--theirs
для сохранения изменений из другой ветки. - Во время перебазирования используйте
--theirs
для сохранения изменений из локальной ветки или--ours
для сохранения изменений из другой ветки. Для объяснения такого обмена см. эту заметку в документации Git.
Если слияние более сложное, можете воспользоваться визуальным редактором различий:
(master*)$ 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 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 master
(master)$ git mv --force myfile MyFile
(master)$ git fetch --all
(master)$ git reset --hard origin/master
(master)$ git rm --cached log.txt
Полагая, что хэш желаемого коммита c5f567:
(master)$ git checkout c5f567 -- file1/to/restore file2/to/restore
Если вы хотите откатить файл к состоянию на 1 коммит раньше, чем c5f567, задайте хэш коммита как c5f567~1:
(master)$ git checkout c5f567~1 -- file1/to/restore file2/to/restore
В 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
f = fetch
loll = log --graph --decorate --pretty=oneline --abbrev-commit
m = merge
one = log --pretty=oneline
outstanding = rebase -i @{u}
s = status
unpushed = log @{u}
wc = whatchanged
wip = rebase -i @{u}
zap = fetch -p
Вы не можете этого сделать! 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 config core.fileMode false
Если Вы хотите задать это поведение по-умолчанию для всех авторизованных пользователей, тогда используйте:
$ git config --global core.fileMode false
Итак, Вы в затруднении - Вы сбросили что-то или Вы слили неправильную ветку, или Вы отправили изменения с принудительной перезаписью и теперь Вы не можете найти свои коммиты. Вы знаете, что в какой-то момент было всё в порядке и Вы хотите вернуться к этому состоянию.
Как раз для этого и нужен git reflog
. reflog
отслеживает все изменения вершины ветки, даже если на эту вершину не указывает ни единая ветвь или метка. В принципе, всякий раз при изменении HEAD, в reflog добавляется новая запись. К сожалению, это работает только с локальными репозиториями и отслеживаются только движения (а не, например, изменения файла, которые не были никуда записаны).
(master)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to master
c10f740 HEAD@{2}: checkout: moving from master to 2.2
reflog выше показывает переход с ветки master на ветку 2.2 и обратно. Затем происходит жесткий сброс на старый коммит. Самое последнее действие находится вверху с надписью HEAD@{0}
.
Если Вы случайно переместитесь назад, то reflog будет содержать коммит (0254ea7), на который указывала ветка master до того, как Вы случайно выбросили 2 коммита.
$ git reset --hard 0254ea7
С помощью git reset
можно вернуть ветку master обратно на коммит, на котором она была прежде. Это обеспечивает безопасность при случайном изменении истории.
(взято из Источник).
- Pro Git - великолепная книга Скотта Чакона и Бена Страуба про Git
- Git Internals - еще одна великолепная книга Скотта Чакона, посвященная Git
- Atlassian's Git tutorial Получите Git сразу с учебниками от начального до продвинутого уровня.
- Изучаем ветвление в Git Интерактивный веб-учебник по ветвлению/слиянию/перебазированию
- Getting solid at Git rebase vs. merge
- git-workflow - Руководство от Aaron Meurer по использованию Git в совместной разработке проектов с открытым исходным кодом
- GitHub как рабочий процесс - Интересный подход к использованию GitHub в качестве рабочего процесса, в частности с пустыми пулл-реквестами
- Githug - Игра для изучения более общих рабочих процессов 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 для просмотра состояния репозитория.