- Доходчивое объяснение Git Reset
- Исходные условия — рабочая директория, индекс и репозиторий
- Внутренняя работа git reset
- Стадия 1. Обновление HEAD — git reset —soft
- Стадия 2. Обновление индекса — git reset —mixed
- Стадия 3. Обновление рабочей директории — git reset —hard
- Применяем наши знания в реальных сценариях
- 1. Упс! Я закоммитил что-то по ошибке
- 2. Упс! Я сделал коммит не в ту ветку, а эти изменения мне нужны в новой ветке
- 3. Упс! Я отправил коммит не в ту ветку, а он мне нужен в другой (уже существующей) ветке
- Итоги
- Configuring git Credentials
- 1. Introduction
- 2. Inputting Credentials
- 3. Storing Credentials
- 3.1. Username and Password in URLs
- 3.2. Credential Contexts
- 4. Credential Helpers
- 4.1. Cache Credential Helper
- 4.2. Store Credential Helper
- 4.3. Custom Credential Helpers
- 5. SSH Keys
- 6. Conclusion
Доходчивое объяснение Git Reset
Перевод статьи «Git Reset Explained – How to Save the Day with the Reset Command».
«Помогите! Я закоммитил не в ту ветку!» «Ну вот, опять… Где мой коммит?» Знакомые ситуации, правда?
Я такое слышал неоднократно. Кто-то окликает меня по имени и просит помочь, когда у него что-то пошло не так с git. И такое происходило не только когда я учил студентов, но также и в работе с опытными разработчиками.
Со временем я стал кем-то вроде «того парня, который разбирается в Git».
Мы используем git постоянно, и обычно он помогает нам в работе. Но порой (и куда чаще, чем нам хотелось бы!) что-то идет не так.
Бывает, мы отправляем коммит не в ту ветку. Бывает, теряем часть написанного кода. А можем и добавить в коммит что-то лишнее.
По git есть много онлайн-ресурсов, и часть из них (например, вот эта статья) фокусируется на том, что делать в таких вот нежелательных ситуациях.
Но мне всегда казалось, что в этих ресурсах не хватает объяснений, почему нужно делать так, а не иначе. Когда приводится набор команд, что делает каждая из них? И вообще, как вы пришли к этим командам?
В прошлом посте я рассказывал о внутреннем устройстве Git. И хотя понимать его полезно, читая теория практически всегда недостаточна. Как применить свои знания внутреннего устройства git и использовать их для решения возникающих проблем?
В этом посте я хотел бы построить мост между теорией и практикой и рассказать о команде git reset . Мы разберем, что делает эта команда, что происходит за кулисами, а также применим эти знания в различных сценариях.
Исходные условия — рабочая директория, индекс и репозиторий
Чтобы разобраться во внутренних механизмах git reset , важно понимать процесс записи изменений внутри git. В частности, я имею в виду записи в рабочей директории, индексе и репозитории.
Если вы хорошо ориентируетесь в этой теме, переходите к следующему разделу. Если же вам нужно более глубокое пояснение, почитайте мой предыдущий пост.
Когда мы работаем над кодом своего проекта, мы делаем это в рабочей директории. Ею может быть любая директория в нашей файловой системе, имеющая привязанный к ней репозиторий. В ней хранятся папки и файлы нашего проекта, а также директория под названием .git.
После того как мы внесли какие-то изменения, мы хотим отправить их в репозиторий. Репозиторий это набор коммитов, каждый из которых представляет собой архив того, как выглядело рабочее дерево проекта на момент создания этого архива (на нашей машине или на чьей-то еще).
Давайте создадим в рабочей директории какой-нибудь файл и запустим команду git status :
Да, git не записал (не закоммитил) изменения, сделанные в рабочей директории, напрямую в репозиторий.
Вместо этого изменения сначала регистрируются в индексе (или в стейджинге). Оба эти термина означают одно и то же, и оба часто используются в документации git. В этой статье мы тоже будем пользоваться обоими, так как они полностью взаимозаменяемы.
Когда мы применяем git add , мы добавляем файлы (или изменения внутри файлов) в стейджинг. Давайте попробуем использовать эту команду для только что созданного файла:
Как показывает git status , наш файл теперь в стейджинге и готов к коммиту. Да, он еще не является частью никакого коммита. Другими словами, сейчас он находится в рабочей директории, а также в индексе, но не в репозитории.
Если мы теперь выполним git commit , мы создадим коммит на основе состояния индекса. Таким образом новый коммит (в примере — commit 3) будет включать файл, который мы чуть ранее добавили в стейджинг.
Рабочая директория находится в точно таком же состоянии, как индекс и репозиторий.
При выполнении git commit текущая ветка master начинает указывать на только что созданный объект commit.
Внутренняя работа git reset
Мне нравится представлять git reset как команду, которая поворачивает вспять описанный выше процесс (внесение изменений в рабочей директории, добавление их в индекс, а затем сохранение в репозиторий).
У git reset есть три режима: —soft , —mixed и —hard . Я рассматриваю их как три стадии:
- Стадия 1. Обновление HEAD — git reset —soft
- Стадия 2. Обновление индекса — git reset —mixed
- Стадия 3. Обновление рабочей директории — git reset —hard
Стадия 1. Обновление HEAD — git reset —soft
Прежде всего, git reset меняет то, на что указывает HEAD. Если мы выполним git reset —hard HEAD
1 , HEAD будет указывать не на master, а на HEAD
1. Если использовать флаг —soft , git reset на этом и остановится.
Если вернуться к нашему примеру, HEAD будет указывать на commit 2, и таким образом new_file.txt не будет частью дерева текущего коммита. Но он будет частью индекса и рабочей директории.
Если посмотреть git status , мы увидим, что этот файл определенно в стейджинге, но не закоммичен.
Иными словами, мы вернули процесс на стадию, где мы уже применили git add , но еще не применяли git commit .
Стадия 2. Обновление индекса — git reset —mixed
Если мы используем git reset —mixed HEAD
1 , git не остановится на обновлении того, на что указывает HEAD. Помимо этого обновится еще и индекс (до состояния уже обновленного HEAD).
В нашем примере это значит, что индекс будет в том же виде, что и commit 2:
Таким образом мы вернули процесс на стадию до выполнения команды git add . Новосозданный файл является частью рабочей директории, но не индекса и не репозитория.
Стадия 3. Обновление рабочей директории — git reset —hard
Если использовать git reset — hard HEAD
1 , то после перевода указателя HEAD (на что бы он ни указывал раньше) на HEAD
1, а также обновления индекса до (уже обновленного) HEAD, git пойдет еще дальше и обновит рабочую директорию до состояния индекса.
Применительно к нашему примеру это означает, что рабочая директория будет приведена к состоянию индекса, который уже приведен в состояние commit 2:
Собственно, мы вернули весь процесс на этап до создания файла my_file.txt.
Применяем наши знания в реальных сценариях
Теперь, когда мы разобрались с тем, как работает git reset , давайте применим эти знания, чтобы спасти какую-нибудь ситуацию!
1. Упс! Я закоммитил что-то по ошибке
Рассмотрим следующий сценарий. Мы создали файл со строкой «This is very importnt», отправили его в стейджинг, а после — в коммит.
А затем — ой! — обнаружили, что в предложении у нас опечатка.
Ну, теперь-то мы знаем, что это можно легко исправить. Мы можем отменить наш последний коммит и вернуть файл в рабочую директорию, используя git reset —mixed HEAD
1 . Теперь моно отредактировать содержимое файла и сделать коммит еще раз.
Совет. В данном конкретном случае мы также можем использовать git commit —amend , как описано здесь.
2. Упс! Я сделал коммит не в ту ветку, а эти изменения мне нужны в новой ветке
Со всеми нами такое случалось. Сделал что-то, закоммитил…
О нет, мы сделали коммит в ветку master, а надо было создать новую и затем сделать пул-реквест.
Я считаю, что здесь будет полезно визуализировать наше положение и то положение, в котором мы хотели бы оказаться.
Собственно, от желаемого состояния нас отделяют три изменения.
- Ветка new должна указывать на наш недавно добавленный коммит.
- Ветка master должна указывать на предыдущий коммит.
- HEAD должен указывать на new.
Мы можем достичь желаемого положения в три шага:
Во-первых, нужно сделать так, чтобы ветка new указывала на недавно добавленный коммит. Достичь этого можно при помощи команды git branch new . Таким образом мы достигаем следующего состояния:
Во-вторых, нужно сделать так, чтобы master указывала на предыдущий коммит (иными словами, на HEAD
1). Достичь этого можно при помощи команды git reset —hard HEAD
1 . Таким образом мы достигаем следующего состояния:
Наконец, мы хотели бы оказаться в ветке new, т. е. сделать так, чтобы HEAD указывал на new . Это легко достижимо путем выполнения команды git checkout new .
- git branch new
- git reset —hard HEAD
3. Упс! Я отправил коммит не в ту ветку, а он мне нужен в другой (уже существующей) ветке
В этом случае мы проходим те же шаги, что и в предыдущем сценарии. Мы проделали какую-то работу и закоммитили изменения…
О нет, мы отправили коммит в ветку master , а нужно было отправить в совсем другую.
Давайте снова изобразим текущее и желаемое положение:
У нас опять же есть три отличия.
Нам нужно, чтобы самый последний коммит оказался в ветке existing. Поскольку в настоящее время на этот коммит указывает master , мы можем попросить git взять последний коммит из ветки master и применить его к ветке existing :
- git checkout existing — переключение на ветку existing ,
- git cherry-pick master — применение последнего коммита в ветке master к текущей ветке ( existing ).
Теперь наше положение следующее:
Все, что нам нужно, это сделать так, чтобы master указывала на предыдущий коммит, а не на самый последний. Для этого:
- git checkout master — смена активной ветки на master ,
- git reset —hard HEAD
1 — теперь мы вернулись к изначальному состоянию этой ветки.
Таким образом мы достигли желаемого положения:
Итоги
В этой статье мы изучили, как работает git reset , а также разобрали три разных режима этой команды: —soft , —mixed и —hard .
Также мы применили свои новые знания для решения жизненных задач.
Понимание работы git позволяет уверенно действовать в любых ситуациях, а также наслаждаться красотой этого инструмента.
Configuring git Credentials
Last modified: February 8, 2021
If you have a few years of experience in the Java ecosystem, and you’re interested in sharing that experience with the community (and getting paid for your work of course), have a look at the «Write for Us» page. Cheers, Eugen
1. Introduction
In recent years, git has seen a sharp rise in popularity over other SCM systems such as subversion. With the rise of free platforms such as GitHub and GitLab, it’s easier than ever to securely version and saves our application code.
But constantly typing in credentials can be cumbersome and hard to crate automated CI/CD pipelines. So in this tutorial, we’ll look at how to configure git credentials to prevent having to enter them manually.
2. Inputting Credentials
Whenever a remote connection requires authentication, git has several ways to look for credentials to use.
Let’s start with the basics, in which no credentials have been configured. If git needs a username and password to access a remote connection, it takes the following steps to prompt the user for input.
First, it tries to invoke an application that allows the users to input credentials. The following values are checked (in order) to determine the application to use:
- GIT_ASKPASS environment variable
- core.askPass configuration variable
- SSH_ASKPASS environment variable
If any of these are set, the application is invoked, and the user’s input is read from its standard output.
If none of these values are set, git reverts to prompting the user for input on the command line.
3. Storing Credentials
Typing in usernames and passwords can be tedious, especially when committing code frequently throughout the day. Typing in passwords manually is error-prone and also makes it difficult to create automated pipelines.
To help with this, git provides several ways to store usernames and passwords. We’ll look at each way in the following sections.
3.1. Username and Password in URLs
Some git providers allow embedding username and password together in the repository URL. This can be done when we clone the repository:
Keep in mind if the password has special characters, they will need to be escaped to prevent the shell from trying to interpret them.
Alternatively, we can edit the git config file inside the repository to include the username and password:
Either way, remember that the username and password are stored in plain text, so anyone with access to the repository would be able to see them.
3.2. Credential Contexts
Git also allows configuring credentials per context. The following command will configure a specific git context to use a specific username:
Alternatively, we can directly edit our global git config file. This is typically found in our home directory in a file named .gitconfig, and we would add the following lines:
This method is also insecure because the username is stored in plain text. It also doesn’t allow storing passwords, so git will continue to prompt for them.
4. Credential Helpers
Git provides credential helpers to save credentials more securely. Credential helpers can store data in multiple ways and even integrate with 3rd party systems like password keychains.
Out of the box, git provides 2 basic credential helpers:
- Cache: credentials stored in memory for short durations
- Store: credentials stored indefinitely on disk
We’ll look at each one next.
4.1. Cache Credential Helper
The cache credential helper can be configured as follows:
The cache credential helper never writes credentials to disk, although the credentials are accessible using Unix sockets. These sockets are protected using file permissions that are limited to the user who stored them, so generally speaking, they are secure.
We can also provide a timeout argument when configuring the cache credential helper. This allows us to control how long the credentials remain in memory:
This will save in memory credentials for 1 day after entering them.
4.2. Store Credential Helper
The store credential helper indefinitely saves credentials to a file. We can configure the store credential helper as follows:
While the file contents are not encrypted, they are protected using file system access controls to the user that created the file.
By default, the file is stored in the user’s home directory. We can override the file location by passing a file argument to the command:
4.3. Custom Credential Helpers
Beyond the two default credential helpers mentioned above, it is possible to configure custom helpers. These allow us to do more sophisticated credential management by delegating to 3rd party applications and services.
Creating custom credential helpers is not something most users will need to worry about. However, there are several reasons they can be helpful:
- Integrate with Operating System tools such as Keychain on macOS
- Incorporate existing corporate authentication schemes such as LDAP or Active Directory
- Provide additional security mechanisms such as two-factor authentication
5. SSH Keys
Most modern git servers provide a way to access repositories using SSH keys instead of username and password over HTTPS. SSH keys are harder to guess than a password and can easily be revoked if they become compromised.
The main downside to using SSH is that it uses non-standard ports. Some networks or proxies may block these ports, making communication with the remote server impossible. They also require additional steps to set up SSH keys on both the server and client, which can be cumbersome in large organizations.
The easiest way to enable SSH for a git repository is to use ssh for the protocol when cloning it:
For an existing repository, we can update the remote with the following command:
The process for configuring SSH keys varies slightly for each git server. In general, the steps are:
- Generate a compatible public/private key combination on your machine
- Upload the public key to your git server
Most Unix/Linux users will already have an SSH key pair created and configured in their home directory and upload the existing public key. As a reminder, we should never upload or otherwise share our private key.
6. Conclusion
In this tutorial, we have seen various ways to configure git credentials. The most common way is to use the built-in credential helper to store credentials locally in memory or a file on disk. A more sophisticated and secure way to store credentials is by using SSH, although this can be more complex and may not work on all networks.