- Git Essentials(Second Edition)
- Ferdinando Santacroce
- 994字
- 2021-07-02 15:27:09
Detached HEAD
Now it's time to explore another important concept about Git and its references, the detached HEAD state.
For the sake of the explanation, go back to the master branch and see what happens when we check out the previous commit, moving HEAD backward; perform a git checkout HEAD^:
[42] ~/grocery (master) $ git checkout HEAD^ Note: checking out 'HEAD^'.
You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b <new-branch-name> HEAD is now at e4a5e7b... Add an apple
Wow, a lot of new things to see here. But don't be scared, it's not that complicated: let's take some baby steps into the long message Git showed us.
First, think about it: Git is very kind and often tells us loads of useful information in its output messages. Don't under evaluate this behavior: especially at the beginning, reading Git messages allows you to learn a lot, so read them carefully.
Here, Git says we are in a detached HEAD state. Being in this state basically means that HEAD does not reference a branch, but directly a commit, the e4a5e7b one in this case; do a git log and see:
[43] ~/grocery ((e4a5e7b...)) $ git log --oneline --graph --decorate --all * a8c6219 (melons) Add a watermelon * ef6c382 (berries) Add a blackberry * 0e8b5cf (master) Add an orange * e4a5e7b (HEAD) Add an apple * a57d783 Add a banana to the shopping list
First of all, in the shell prompt you can see that between rounds, that now are doubled, there is not a branch name, but the first seven characters of the commit, ((e4a5e7b...)).
Then, HEAD is now stuck to that commit, while branches, especially the master one, remains at their own place. As a result, the HEAD file now contains the hash of that commit, not a ref to a branch as before:
[44] ~/grocery ((e4a5e7b...)) $ cat .git/HEAD e4a5e7b3c64bee8b60e23760626e2278aa322f05
Going on, Git says that in this state we can look around, make experiments, doing new commits if we like, and then discard them simply by checking out an existing branch, or save them if you like creating a new branch. Can you say why this is true?
Due to reachability of commits, of course. If we do some commits, then move HEAD to an existing branch, those commits become unreachable. They stay in a reachable state until HEAD is on top of the last of them, but when you move HEAD with a git checkout, they are gone. At the same time, if you create a new branch before moving HEAD, there will be a label, a pointer Git can use to reach those commits, so they are safe.
Want to try?
Okay, let's have some fun; modify the shoppingList.txt file, adding a bug:
[45] ~/grocery ((e4a5e7b...)) $ echo "bug" > shoppingList.txt
Then commit this voluntary mistake:
[46] ~/grocery ((e4a5e7b...)) $ git commit -am "Bug eats all the fruits!" [detached HEAD 07b1858] Bug eats all the fruits! 1 file changed, 1 insertion(+), 2 deletions(-)
Let's cat the file:
[47] ~/grocery ((07b1858...)) $ cat shoppingList.txt bug
Ouch, we actually erased all your shopping list files!
What happened in the repository then?
[48] ~/grocery ((07b1858...))
$ git log --oneline --graph --decorate --all
* 07b1858 (HEAD) Bug eats all the fruits! | * a8c6219 (melons) Add a watermelon | * ef6c382 (berries) Add a blackberry | * 0e8b5cf (master) Add an orange |/
* e4a5e7b Add an apple * a57d783 Add a banana to the shopping list
Nice! We have a new commit, the bug one, and we can see as HEAD followed us, so now it points to it. Then, the console drew two different paths, because starting from apple commit, we traced two routes: one that goes to the master branch (then to berries and melons), and one that goes to the bug commit we just made.
Okay, so if we now check out master again, what happens? Give it a try:
[49] ~/grocery ((07b1858...)) $ git checkout master
Warning: you are leaving 1 commit behind, not connected to any of your branches: 07b1858 Bug eats all the fruits! If you want to keep it by creating a new branch, this may be a good time to do so with:
git branch <new-branch-name> 07b1858 Switched to branch 'master'
Okay, we have already seen this message: Git is aware that we are leaving a commit behind; but in this case, it's not a problem for us, indeed it's actually what we really want.
Let's check the situation:
[50] ~/grocery (master) $ git log --oneline --graph --decorate --all * a8c6219 (melons) Add a watermelon * ef6c382 (berries) Add a blackberry * 0e8b5cf (HEAD -> master) Add an orange * e4a5e7b Add an apple * a57d783 Add a banana to the shopping list
Yay! The bug commit is gone, so nothing is compromised. In the previous message, Git was kind enough to remind us how to recover that commit, just in case; the trick is to directly create a branch that points to that commit, and Git pinned us even the complete command. Let's try it, creating a bug branch:
[51] ~/grocery (master) $ git branch bug 07b1858
Let's see what happened:
[52] ~/grocery (master) $ git log --oneline --graph --decorate --all * 07b1858 (bug) Bug eats all the fruits! | * a8c6219 (melons) Add a watermelon | * ef6c382 (berries) Add a blackberry | * 0e8b5cf (HEAD -> master) Add an orange |/ * e4a5e7b Add an apple * a57d783 Add a banana to the shopping list
Wow, that's amazingly simple! The commit is here again, and now we have even a branch to check out if we like.
- SQL Server 2012數據庫技術與應用(微課版)
- MySQL基礎教程
- MySQL從入門到精通(第3版)
- 數據庫系統原理及應用教程(第4版)
- Learn Unity ML-Agents:Fundamentals of Unity Machine Learning
- INSTANT Cytoscape Complex Network Analysis How-to
- OracleDBA實戰攻略:運維管理、診斷優化、高可用與最佳實踐
- 數據庫技術及應用教程
- 數字媒體交互設計(初級):Web產品交互設計方法與案例
- INSTANT Apple iBooks How-to
- 數據庫技術及應用
- R Machine Learning Essentials
- Access 2010數據庫程序設計實踐教程
- 數據分析思維:產品經理的成長筆記
- 數據會說話:活用數據表達、說服與決策