Head detached from как исправить

Detached head means you are no longer on a branch, you have checked out a single commit in the history (in this case the commit previous to HEAD, i.e. HEAD^).

If you want to delete your changes associated with the detached HEAD

You only need to checkout the branch you were on, e.g.

git checkout master

Next time you have changed a file and want to restore it to the state it is in the index, don’t delete the file first, just do

git checkout -- path/to/foo

This will restore the file foo to the state it is in the index.

If you want to keep your changes associated with the detached HEAD

  1. Run git branch tmp – this will save your changes in a new branch called tmp.
  2. Run git checkout master
  3. If you would like to incorporate the changes you made into master, run git merge tmp from the master branch. You should be on the master branch after running git checkout master.

Community's user avatar

answered Apr 19, 2012 at 13:32

ralphtheninja's user avatar

ralphtheninjaralphtheninja

126k20 gold badges111 silver badges121 bronze badges

34

If you have changed files you don’t want to lose, you can push them. I have committed them in the detached mode and after that you can move to a temporary branch to integrate later in master.

git commit -m "....."
git branch my-temporary-work
git checkout master
git merge my-temporary-work

Extracted from:

What to do with commit made in a detached head

Community's user avatar

answered Dec 9, 2013 at 20:05

Toni Gamez's user avatar

Toni GamezToni Gamez

6,7891 gold badge22 silver badges18 bronze badges

9

A solution with no temporary branch or merge commit

How to exit (“fix”) detached HEAD state when you already changed something in this mode and, optionally, want to save your changes:

  1. Commit changes you want to keep. If you want to take over any of the changes you made in detached HEAD state, commit them. For example:

    git commit -a -m "your commit message"
    
  2. Discard changes you do not want to keep. The hard reset will discard any uncommitted changes that you made in detached HEAD state:

    git reset --hard
    

    (Without this, step 3 would fail, complaining about modified uncommitted files in the detached HEAD.)

  3. Check out your branch. Exit detached HEAD state by checking out the branch you worked on before, for example:

    git checkout master
    
  4. Take over your commits. You can now take over the commits you made in detached HEAD state by cherry-picking, as shown in my answer to another question.

    git reflog
    git cherry-pick <hash1> <hash2> <hash3> ...
    

answered Jun 11, 2013 at 13:23

tanius's user avatar

taniustanius

13.1k3 gold badges49 silver badges61 bronze badges

3

Detached head means:

  1. You are no longer on a branch,
  2. You have checked out a single commit in the history

If you have no changes: you can switch to master by applying the following command

  git checkout master

If you have changes that you want to keep:

In case of a detached HEAD, commits work like normal, except no named branch gets updated. To get master branch updated with your committed changes, make a temporary branch where you are (this way the temporary branch will have all the committed changes you have made in the detached HEAD), then switch to the master branch and merge the temporary branch with the master.

git branch  temp
git checkout master
git merge temp

Peter Mortensen's user avatar

answered Aug 29, 2016 at 0:39

Razan Paul's user avatar

Razan PaulRazan Paul

13.6k3 gold badges69 silver badges61 bronze badges

2

HEAD is a pointer, and it points — directly or indirectly — to a particular commit:

Attached  HEAD means that it is attached to some branch (i.e. it points to a branch).
Detached HEAD means that it is not attached to any branch, i.e. it points directly to some commit.

enter image description here

In other words:

  • If it points to a commit directly, the HEAD is detached.
  • If it points to a commit indirectly, (i.e. it points to a branch, which in turn points to a commit), the HEAD is attached.

To better understand situations with attached / detached HEAD, let’s show the steps leading to the quadruplet of pictures above.

We begin with the same state of the repository (pictures in all quadrants are the same):

enter image description here


Now we want to perform git checkout — with different targets in the individual pictures (commands on top of them are dimmed to emphasize that we are only going to apply those commands):

enter image description here


This is the situation after performing those commands:

enter image description here

As you can see, the HEAD points to the target of the git checkout command — to a branch (first 3 images of the quadruplet), or (directly) to a commit (the last image of the quadruplet).

The content of the working directory is changed, too, to be in accordance with the appropriate commit (snapshot), i.e. with the commit pointed (directly or indirectly) by the HEAD.


So now we are in the same situation as in the start of this answer:

enter image description here

answered Sep 27, 2019 at 23:29

MarianD's user avatar

MarianDMarianD

12.7k12 gold badges41 silver badges54 bronze badges

7

If you made changes and then realized that you are on a detached head, you can do: stash -> checkout master -> stash pop:

git stash
git checkout master   # Fix the detached head state
git stash pop         # Or for extra safety use 'stash apply' then later 
                      #   after fixing everything do 'stash drop'

You will have your uncommited changes and normal “attached” HEAD, like nothing happened.

answered Jan 1, 2015 at 22:40

mojuba's user avatar

mojubamojuba

11.8k9 gold badges51 silver badges71 bronze badges

4

Here’s what I just did after I realized I was on a detached head and had already made some changes.

I committed the changes.

$ git commit -m "..."
[detached HEAD 1fe56ad] ...

I remembered the hash (1fe56ad) of the commit. Then I checked out the branch I should have been on.

$ git checkout master
Switched to branch 'master'

Finally I applied the changes of the commit to the branch.

$ git cherry-pick 1fe56ad
[master 0b05f1e] ...

I think this is a bit easier than creating a temporary branch.

answered Aug 2, 2014 at 23:33

Philippe Gerber's user avatar

Philippe GerberPhilippe Gerber

17.4k6 gold badges44 silver badges40 bronze badges

6

When you check out a specific commit in git, you end up in a detached head state…that is, your working copy no longer reflects the state of a named reference (like “master”). This is useful for examining the past state of the repository, but not what you want if you’re actually trying to revert changes.

If you have made changes to a particular file and you simply want to discard them, you can use the checkout command like this:

git checkout myfile

This will discard any uncommitted changes and revert the file to whatever state it has in the head of your current branch. If you want to discard changes that you have already committed, you may want to use the reset command. For example, this will reset the repository to the state of the previous commit, discarding any subsequent changes:

git reset --hard HEAD^

However, if you are sharing the repository with other people, a git reset can be disruptive (because it erases a portion of the repository history). If you have already shared changes with other people, you generally want to look at git revert instead, which generates an “anticommit” — that is, it creates a new commit that “undoes” the changes in question.

The Git Book has more details.

answered Apr 19, 2012 at 13:32

larsks's user avatar

larskslarsks

267k40 gold badges382 silver badges386 bronze badges

1

Since “detached head state” has you on a temp branch, just use git checkout - which puts you on the last branch you were on.

yyny's user avatar

yyny

1,58317 silver badges20 bronze badges

answered Nov 20, 2015 at 19:30

Mike's user avatar

MikeMike

1,8642 gold badges24 silver badges42 bronze badges

3

you probably did git reset --hard origin/your-branch.

Try to just git checkout your-branch

answered Apr 1, 2020 at 11:00

Johnny Cage's user avatar

Johnny CageJohnny Cage

5,2662 gold badges11 silver badges7 bronze badges

0

Being in “detached head” means that HEAD refers to a specific unnamed commit (as opposite to a named branch) (cf: https://git-scm.com/docs/git-checkout section Detached head).
In reality, this means that you have checked out a commit but there is no branch name associated with it.

You may choose to only create a new branch associated with your commit by

git branch new-branch-name.

This allows you to save your current state in a new branch named new-branch-name and not be in a detached head state anymore.

Or if you would like to come back to the previous state, you need to select the branch that was selected before by

git checkout @{-1}

answered Feb 15, 2019 at 15:16

Pat. ANDRIA's user avatar

Pat. ANDRIAPat. ANDRIA

2,3221 gold badge12 silver badges27 bronze badges

To further clarify @Philippe Gerber’s answer, here it is:

git cherry-pick

Before cherry-pick, a git checkout master is necessary in this case. Furthermore, it is only needed with a commit in detached head.

answered Dec 6, 2017 at 13:39

Timo's user avatar

TimoTimo

2,8563 gold badges29 silver badges27 bronze badges

Addendum

If the branch to which you wish to return was the last checkout that you had made, you can simply use checkout @{-1}. This will take you back to your previous checkout.

Further, you can alias this command with, for example, git global --config alias.prev so that you just need to type git prev to toggle back to the previous checkout.

answered Jan 22, 2016 at 11:09

David Brower's user avatar

David BrowerDavid Brower

2,8482 gold badges25 silver badges30 bronze badges

1

Detached head means you have not checked out your branch properly or you have just checked out a single commit.

If you encounter such an issue then first stash your local changes so that you won’t lose your changes.

After that… checkout your desired branch using the command:

Let’s say you want branch MyOriginalBranch:

git checkout -b someName origin/MyOriginalBranch

Leniel Maccaferri's user avatar

answered Jan 13, 2020 at 6:35

Dhirendra Gautam's user avatar

To add to @ralphtheninja’s answer. If you get this message after using git checkout master:

Please commit your changes or stash them before you switch branches.
Aborting

Then you can simply force the checkout using the -f flag as follows:

git checkout -f master

Apparently this will result in losing all the changes made in the detached mode. So be careful when using it.

answered Jan 25, 2022 at 20:12

Souhaib's user avatar

SouhaibSouhaib

1191 silver badge6 bronze badges

Git told me how to do it.

if you typed:

git checkout <some-commit_number>

Save the status

git add .
git commit -m "some message"

Then:

 git push origin HEAD:<name-of-remote-branch>

answered Jan 31, 2019 at 19:30

Sterling Diaz's user avatar

Sterling DiazSterling Diaz

3,7612 gold badges31 silver badges35 bronze badges

1

This approach will potentially discard part of the commit history, but it is easier in case the merge of the old master branch and the current status is tricky, or you simply do not mind losing part of the commit history.

To simply keep things as currently are, without merging, turning the current detached HEAD into the master branch:

  1. Manually back up the repository, in case things go unexpectedly wrong.
  2. Commit the last changes you would like to keep.
  3. Create a temporary branch (let’s name it detached-head) that will contain the files in their current status:
git checkout -b detached-head
  1. (a) Delete the master branch if you do not need to keep it
git branch -D master
  1. (b) OR rename if you want to keep it
git branch -M master old-master
  1. Rename the temporary branch as the new master branch
git branch -M detached-head master

Credit: adapted from this Medium article by Gary Lai.

answered Sep 29, 2020 at 16:37

alexhg's user avatar

alexhgalexhg

6707 silver badges11 bronze badges

0

I was in a similar situation.
For some reason I ended up with a detached head – I had made commits on the same path as the branch I thought I was on – eg HEAD was a child of the branch tag but for some reason the branch tag had stayed back at a historic commit… possibly because I had pushed??

It wouldn’t let me push because I wasn’t considered to be on the branch I thought I was on.

I didn’t want to change any of my history or do any cherry picking and I’d just spent about 8 weeks working on the branch so reset --hard was making me a bit nervous!

The solution was just to do the following:

git branch -f myStuckBranch HEAD
git checkout myStuckBranch

You need to do the checkout even though HEAD and myStuckBranch are now pointing at the same thing because you are still considered to be in the detached head state (not on a branch)

I’m not an expert with git (having mostly used mercurial which would never create this weird situation) but my understanding of this command is that it just says
“change myStuckBranch to point at HEAD”.

I routinely find myself using this command to merge in changes from master after fetching without having to swap my working directory – otherwise it tries to use the old (uninteresting) version of master:

git fetch
git branch -f master origin/master  -- err yeah don't just ignore what's been going on remotely - eg point my master at the real master
git merge master -- merge the changes into my local branch

It’s a bit annoying to have to manually have to do that all the time but still better than having to change your working directory just to update another branch in order to merge in changes from it.

answered Mar 31, 2021 at 14:41

JonnyRaa's user avatar

JonnyRaaJonnyRaa

7,3295 gold badges44 silver badges49 bronze badges

4

Normally HEAD points to a branch. When it is not pointing to a branch instead when it points to a commit hash like 69e51 it means you have a detached HEAD. You need to point it two a branch to fix the issue. You can do two things to fix it.

  1. git checkout other_branch // Not possible when you need the code in that commit hash
  2. create a new branch and point the commit hash to the newly created branch.

HEAD must point to a branch, not a commit hash is the golden rule.

answered Jan 5, 2020 at 7:39

Krishnadas PC's user avatar

Krishnadas PCKrishnadas PC

5,8012 gold badges53 silver badges50 bronze badges

1

This was a confusing thing to me when I started to work with git and later I figure out why this is happening and what is the best way to deal with such a situation.

The root cause for such occurrence is that normally git HEAD is always pointing to some branch and when you try to point the HEAD to some specific commit, you put HEAD into a detached HEAD state.

When HEAD is attached state –

cat .git/HEAD     # output--> ref: refs/heads/master or ref: refs/heads/main

When HEAD is detached state –

cat .git/HEAD     # output--> b96660a90cad75867453ebe1b8d11754bbb68b0e <commit hash>

Solution –

git stash           # Temporarily shelves (or stashes) changes
git branch          # Find your default branch
git switch master   # Point HEAD to master or main branch
git stash pop       # Apply all the changes you had previously

answered Sep 24, 2022 at 0:44

Aravinda Meewalaarachchi's user avatar

Git : You are not currently on a branch.

Time to time Git shows :

To push the history leading to the current (detached HEAD)
state now, use

git push origin HEAD:<name-of-remote-branch>

It means :

  • ” HEAD have no branch “

To fix that run 2 commands :

  1. git branch -f {{your_working_branch}} HEAD — set branch to your head
  2. git checkout {{your_working_branch}} — checkout==switch branch

answered Oct 11, 2022 at 3:21

Bruno's user avatar

BrunoBruno

6,3755 gold badges40 silver badges47 bronze badges

I wanted to keep my changes so, I just fix this doing…

git add .
git commit -m "Title" -m "Description"
(so i have a commit now example: 123abc)
git checkout YOURCURRENTBRANCH
git merge 123abc
git push TOYOURCURRENTBRANCH

that work for me

Paul Rooney's user avatar

Paul Rooney

20.6k9 gold badges39 silver badges61 bronze badges

answered Dec 4, 2018 at 22:11

CRLZXO's user avatar

When you’re in a detached head situation and created new files, first make sure that these new files are added to the index, for example with:

git add .

But if you’ve only changed or deleted existing files, you can add (-a) and commit with a message (-m) at the the same time via:

git commit -a -m "my adjustment message"

Then you can simply create a new branch with your current state with:

git checkout -b new_branch_name

You’ll have a new branch and all your adjustments will be there in that new branch. You can then continue to push to the remote and/or checkout/pull/merge as you please.

answered Mar 21, 2019 at 22:15

DZet's user avatar

DZetDZet

5393 silver badges10 bronze badges

Realizing I had a detached head without knowing how I managed to get it (like three commits away), I also found out that trying to merge, rebase or cherry-pick triggered hundreds of merge-conflicts, so I took a different approach:

  1. (Assuming everything is committed (working tree is “clean”))

  2. Save my commit messages: git log > /tmp/log

  3. Save my working tree: mkdir /tmp/backup && cp -a all_my files_and_directories /tmp/backup

  4. Revert to master: git checkout master

  5. Remove all the working files and directories: rm ...

  6. Use the backup: cp -a /tmp/backup/. .

  7. git add and git commit using messages from saved /tmp/log, maybe repeating it with different sub-sets of files…

The disadvantage is that you loose your commit history if one file was changed multiple times since master, but in the end I had a clean master.

answered Jul 27, 2020 at 12:21

U. Windl's user avatar

U. WindlU. Windl

3,33325 silver badges53 bronze badges

The detached HEAD means that you are currently not on any branch. If you want to KEEP your current changes and simply create a new branch, this is what you do:

git commit -m "your commit message"
git checkout -b new_branch

Afterwards, you potentially want to merge this new branch with other branches. Always helpful is the git “a dog” command:

git log --all --decorate --oneline --graph

answered Apr 21, 2020 at 10:00

gebbissimo's user avatar

gebbissimogebbissimo

2,0172 gold badges24 silver badges32 bronze badges

If you hate head and wanna go back to main instead:

git checkout main

If you love head but just wish main tracked it:

git checkout -B main HEAD

(This works with any branch name, not just main, and for any commit pointer, not just HEAD.)

(If you have uncommitted changes that you want to commit, commit them first.)

answered Feb 11 at 21:57

Sandra's user avatar

SandraSandra

2293 silver badges7 bronze badges

This works for me, It will assign a new branch for detached head :

git checkout new_branch_name detached_head_garbage_name

lenooh's user avatar

lenooh

10.3k5 gold badges58 silver badges49 bronze badges

answered Mar 17, 2020 at 17:06

Alok Gupta's user avatar

Alok GuptaAlok Gupta

1,78821 silver badges21 bronze badges

1

With git rebase you can move your HEAD to the desired commit

Suppose you have your branch in a detached state, like this:

* bfcb8f9 Commit 4
* 540a123 Commit 3
* 4356d64 Commit 2
| * fecb8d2 Commit 2
|/
| * 8012f45 Commit 2x
|/
| * 6676d15 (HEAD -> master) Commit 2 --amend
|/
* 1818f91 Commit 1

The detached head was created by rebasing by mistake, pointing to a detached commit, which was created previously due a git commit –amend command.

If you want to move your HEAD ref to the most recent commit, apply a rebase with the desired HASH commit you want to point to. In this example, the hash is of the most recent commit:

git rebase bfcb8f9

and this will leave your branch with its HEAD poiting to the desired commit (the most recent in this case):

* bfcb8f9 (HEAD -> master) Commit 4
* 540a123 Commit 3
* 4356d64 Commit 2 --amend
| * fecb8d2 Commit 2
|/
| * 8012f45 Commit 2x
|/
| * 6676d15 Commit 2
|/
* 1818f91 Commit 1

answered Jul 12, 2021 at 16:48

Edgar Alejandro Ramirez's user avatar

git pull origin master

worked for me. It was just about giving remote and branch name explicitly.

answered May 14, 2014 at 14:14

Amjedonline's user avatar

1

I came here because I had the HEAD detached at... message.

In my case it wasn’t due to some local changes because, on that system I never do any changes.

The reason for my message was: I accidently checked out origin/myBranch instead of myBranch.

So when checking out myBranch everything returned to normal.

answered Apr 21 at 18:44

Skeeve's user avatar

SkeeveSkeeve

7,0082 gold badges16 silver badges26 bronze badges

1400761301_tudaТы туда не ходи – ты сюда ходи. А то снег башка попадёт – совсем мёртвый будешь…

В этой статье пойдет речь про отрубленное отсоединенное состояние указателя HEAD. Надо понимать что названия веток в Git это указатели на коммиты. Имя ветки, допустим master, указывает на последний коммит в ветке master.

Существует так же множество других указателей и один из них HEAD, но это очень важный указатель. И вот о нем мы и поговорим.

И так у нас есть история коммитов

H0001

И сейчас указатель ветки master и указатель HEAD указывают на последний коммит С4 в ветке master.

То же самое можно увидеть в Git

H0002

Оба указателя master и HEAD указывают на коммит С4 (efaaf18).

Теперь передвинем указатель HEAD на коммит C2 командой

$ git checkout 7bbbd68

H0003

Вот мы и получили отрубленную отсоединеннуюый голову указатель HEAD. И Git нам любезно сообщил что снег башка попал…

На диаграмме это можно изобразить так

H0004

То есть команда git checkout 7bbbd68 просто перенесла указатель HEAD на коммит C2 (7bbbbd68). И вместе с этим вернула в рабочий каталог состояние файлов этого коммита. То есть мы откатились назад по истории коммитов.

И Git порекомендовал нам создать новую ветку, а так же сообщил хэш коммита на который сейчас указывает HEAD.

Все это можно увидеть в Git

H0005

Состояние HEAD detached означает что HEAD указывает не на вершину какой либо ветки, а просто на какой-то коммит.

Посмотреть историю перемещения головы можно командой git reflog

H0006

На скрине мы так же посмотрели состояние Git и он нам (аж красным) сообщил, что башка отсоединена на коммит 7bbbd68.

HEAD всегда должен указывать на вершину какой-либо ветки. Это очень важно. И вот почему.

Указатель HEAD по существу указывает на тот коммит, после которого будет сделан следующий коммит. И если в состоянии отрубленной головы мы сейчас сделаем еще один коммит, то у нас будет шанс потерять его, не в смысле что он будет не доступен, а что если мы не будем помнить хэш этого коммита, то мы ни когда не сможем на него переключится.

Давайте сделаем коммит и посмотрим что будет. Изменим файлик и посмотрим статус

H0007

Ну и делаем коммит

H0008

Из лога коммитов видно что сейчас HEAD указывает на коммит С5 (84b361c), но на этот коммит не указывает ни какая ветка, верней сказать ни какой указатель ветки.

Графически это можно изобразить так

H0009

Мы по прежнему находимся в состоянии detached HEAD, о чем нам все время напоминает Git. Еще раз напомню что это означает что HEAD указывает не на вершину какой-либо ветки, а просто на коммит. В нашем случае на коммит С5 (84b361c).

Выйти из состояния detached HEAD очень легко, для этого надо переключится на какую-либо ветку или создать новую ветку.

Давайте переключимся на ветку master командой git checkout master

H0010

При переключении на ветку master, Git заботливо нас предупредил, что мы оставляем 1 commit, который не присоединен ни к какой ветке. Сообщил нам имя этого коммита – С5 и его хэш – 84b361c. И посоветовал, что возможно уже самое время создать новую ветку командой

git branch new_branch_name 84b361c

Поскольку если мы сейчас не создадим ветку (указатель) на этом коммите, то мы можем его потерять.

Если сейчас дать команду просмотра лога коммитов, то мы коммит С5 в логе не увидим

H0011

Таким образом мы можем “потерять” коммит С5, если забудем его хэш. Конечно, как уже говорилось, что в Git какой-либо сделанный коммит сложно потерять (но можно). И в данном случае мы можем посмотреть историю перемещения HEAD

H0012

и в ней мы можем увидеть наш потерянный коммит, хотя мы его и не видели в истории коммитов (логе коммитов).

Теперь дадим команду

$ git branch lost_branch 84b361c

и посмотрим лог

H0013

Теперь, все хорошо. На коммит С5 указывает ветка lost_branch

Графически это выглядит так

H0014

Теперь мы легко можем переключится на ветку lost_branch и состояния HEAD deatached уже не будет, поскольку HEAD уже будет указывать на вершину ветки lost_branch.

H0015

Сейчас переключение произошло безболезненно, поскольку это вполне штатная работа в Git.

Ну и покажу это состояние графически

H0016

На этом, с отсоединенной головой пока все. Хотя есть еще варианты по отделению головы в Git, но об этом как-нибудь в другой раз.

If git was to rename detached HEAD I would have it named as a HEAD that isn’t identified by a branch and will soon be forgotten.

We as people can easily remember branch names. We do git checkout new-button-feature / git checkout main. main and new-button-feature are easy to remember. And we can just do git branch and get a list of all branches. But to do the same with just commits you’d have to do git reflog which is very tedious. Because you have thousands of commits but only very few branches.

A detached commit’s identifier is just its SHA. So suppose you checked out a commit (not a branch) i.e. you did git checkout d747dd10e450871928a56c9cb7c6577cf61fdf31 you’ll get:

Note: checking
out ‘d747dd10e450871928a56c9cb7c6577cf61fdf31’.

You are in ‘detached HEAD’ state.

Then if you made some changes and made a commit, you’re still NOT on a branch.

Do you think you’d remember the commit SHA? You won’t!

git doesn’t want this to happen. Hence it’s informing your HEAD is not associated to a branch so you’re more inclined to checkout a new branch. As a result below that message it also says:

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


To go a bit deeper a branch is built in a way that it’s smart. It will update its HEAD as you make commits. Tags on the other hand are not meant to be like that. If you checkout a tag, then you’re again on a detached HEAD. The main reason is that if you make a new commit from that tag then given that that commit is not referenced by anything (not any branch or tag) then still its considered a detached HEAD.

Attached HEADs can only happen when you’re on a branch.

For more see here

HEAD is a pointer, and it points — directly or indirectly — to a
particular commit:

Attached HEAD means that it is attached to some branch (i.e. it
points to a branch).

Detached HEAD means that it is not attached to any branch, i.e. it
points directly to some commit.

To look at from another angle, if you’re on a branch and do cat .git/HEAD you’d get:

ref: refs/heads/Your-current-branch-name

Then if you do cat refs/heads/Your-current-branch-name then you’d also see the SHA of the commit that your branch is pointing/referencing to.

However if you were on a detached HEAD you and cat .git/HEAD you’d just get the SHA of the commit and nothing more:

639ce5dd952a645b7c3fcbe89e88e3dd081a9912

By nothing more I mean the head isn’t pointing to any branch. It’s just directly pointing to a commit.


As a result of all this, anytime you checkout a commit (without using the branch name to checkout), even if that commit was the latest commit of your main branch, you’re still in a detached HEAD because your HEAD is not pointing to any of your local branches. Hence even checking out a tag will put you in a detached HEAD. To add onto that, even checking out a remote branch that you have fetched into your computer would result in a detached head ie git checkout origin main would also end up as a detached head…

Summary

All of the following will cause detached head:

  • checkout any commit
  • checkout any tag
  • checkout any remote branch

You’re only on an attached head, if you’ve checked out a local branch


Special thanks to Josh Caswell & Saagar Jha in helping me figure this out.

“Git detached head” is a weird error message to receive…

The following is a guest blog post written by Carlos Schults.

Newcomers to Git often get confused with some of the messages that the VCS tool throws at them. The “You are in ‘detached HEAD’ state” one is certainly one of the weirdest. After coming across this message, most people start furiously Googling “git detached HEAD,” “git detached HEAD fix,” or similar terms, looking for anything that might be of help. If that’s your case, you’ve come to the right place.

Here’s the first thing you should know: you haven’t done anything wrong. Your repo isn’t broken or anything like that. The expression “Detached HEAD” might sound somewhat bizarre, but it’s a perfectly valid repository state in Git. Sure, it’s not the normal state, which would be—you’ve guessed it!—when HEAD is attached. The second thing you need to know is that going back to normal is super easy. If you just want to do that and get on with your day, go to the “How Do I Fix a Detached Head in Git?” section of this post to see how it’s done.

But if you want to know more—and I guess you do—stick around and we’ll help you. What does HEAD mean in Git? What does it mean for it to be attached or detached? These are the kind of questions we’ll answer in this post. By the end of it, you’ll have a better understanding of Git’s fundamentals, and detached HEADs will never trouble you again. Let’s dig in.

Git Detached HEAD: Reproducing the “Problem”

Let’s start with a quick demo showing how to reach the detached HEAD state. We’ll create a repository and add some commits to it:

mkdir git-head-demo
cd git-head-demo
git init
touch file.txt
git add .
git commit -m "Create file"
echo "Hello World!" > file.txt
git commit -a -m "Add line to the file"
echo "Second file" > file2.txt
git add .
git commit -m "Create second file"

With the commands above, we’ve created a new folder with a new repository inside it. Then we created a new empty file and committed that with the message “Create file.” Next, we added a line to that file and committed the change, with the message “Add a line to the file.” Finally, we’ve created another file with one line of text and committed that as well. If you use the git log –oneline command, you’ll see something like this:

Let’s say that, for testing purposes, we need to see how things were at the time of the second commit. How would we do that? As it turns out, we can check out a commit the same way we’d check out branches. Remember, branches are just names for commits. So, based on the example output above, we’d run git checkout 87ec91d. Keep in mind that if you’re following along by executing these commands on your own system, the hash for your commits will be different from those in the example. Use the log command to find it.

After running the command, we get the “You are in ‘detached HEAD’ state[…]” message. Before we go on, make sure you keep this in mind: you get to the detached HEAD state by checking out a commit directly.

What Is a HEAD in Git?

What does HEAD mean in Git? To understand that, we have to take a step back and talk fundamentals.

A Git repository is a collection of objects and references. Objects have relationships with each other, and references point to objects and to other references. The main objects in a Git repository are commits, but other objects include blobs and trees. The most important references in Git are branches, which you can think of as labels you put on a commit.

HEAD is another important type of reference. The purpose of HEAD is to keep track of the current point in a Git repo. In other words, HEAD answers the question, “Where am I right now?”

For instance, when you use the log command, how does Git know which commit it should start displaying results from? HEAD provides the answer. When you create a new commit, its parent is indicated by where HEAD currently points to.

Are you in ‘detached HEAD’ state?

You’ve just seen that HEAD in Git is only the name of a reference that indicates the current point in a repository. So, what does it mean for it to be attached or detached?

Most of the time, HEAD points to a branch name. When you add a new commit, your branch reference is updated to point to it, but HEAD remains the same. When you change branches, HEAD is updated to point to the branch you’ve switched to. All of that means that, in these scenarios, HEAD is synonymous with “the last commit in the current branch.” This is the normal state, in which HEAD is attached to a branch.

A visual representation of our demo repository would look like this:

As you can see, HEAD points to the controller branch, which points to the last commit. Everything looks perfect. After running git checkout 87ec91d, the repo looks like this:

This is the detached HEAD state; HEAD is pointing directly to a commit instead of a branch.

Benefits of a Git Detached HEAD

Are there good reasons for you to be in the detached HEAD state? You bet there are!

As you’ve seen, you detach the HEAD by checking out a commit. That’s already useful by itself since it allows you to go to a previous point in the project’s history. Let’s say you want to check if a given bug already existed last Tuesday. You can use the log command, filtering by date, to start the relevant commit hash. Then you can check out the commit and test the application, either by hand or by running your automated test suite.

What if you could not only take a look at the past, but also change it? That’s what a detached HEAD allows you to do. Let’s review how the detached message starts:

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 switching back to a branch.

In this state, you can make experimental changes, effectively creating an alternate history. So, let’s create some additional commits in the detached HEAD state and see how our repo looks afterward:

echo "Welcome to the alternate timeline, Morty!" > new-file.txt
git add .
git commit -m "Create new file"
echo "Another line" >> new-file.txt
git commit -a -m "Add a new line to the file"

We now have two additional commits that descend from our second commit. Let’s run git log –oneline and see the result:

This is what the diagram looks like now:

What should you do if you want to keep those changes? And what should you do if you want to discard them? That’s what we’ll see next.

How Do I Fix a Detached HEAD in Git?

You can’t fix what isn’t broken. As I’ve said before, a detached HEAD is a valid state in Git. It’s not a problem. But you may still want to know how to get back to normal, and that depends on why you’re in this situation in the first place.

Scenario #1: I’m Here by Accident

If you’ve reached the detached HEAD state by accident—that is to say, you didn’t mean to check out a commit—going back is easy. Just check out the branch you were in before:

git checkout <branch-name>

If you’re using Git 2.23.0 or newer, you can also use switch instead of checkout:

git switch <branch-name>

Scenario #2: I’ve Made Experimental Changes and I Want to Discard Them

You’ve entered the detached HEAD state and made a few commits. The experiment went nowhere, and you’ll no longer work on it. What do you do? You just do the same as in the previous scenario: go back to your original branch. The changes you made while in the alternate timeline won’t have any impact on your current branch.

Scenario #3: I’ve Made Experimental Changes and I Want to Keep Them

If you want to keep changes made with a detached HEAD, just create a new branch and switch to it. You can create it right after arriving at a detached HEAD or after creating one or more commits. The result is the same. The only restriction is that you should do it before returning to your normal branch.

Let’s do it in our demo repo:

git branch alt-history
git checkout alt-history

Notice how the result of git log –oneline is exactly the same as before (the only difference being the name of the branch indicated in the last commit):

We could just run git branch alt-history, and we’d be all set. That’s the new—and final—version of our diagram:

Getting Rid of the “Git Detached HEAD” Message

Before wrapping up, let’s share a final quick tip. Now that you understand everything about detached HEAD in Git and know that it’s not that big of a deal, seeing that message every time you check out a commit might become tiring. Fortunately, there’s a way to not see the warning anymore. Just run the following command:

git config advice.detached head false

Easy, right? You can also use the –global modifier if you want the change to apply to every repository and not only the current one.

Git Detached HEAD: Less Scary Than It Sounds

A message talking about heads not being attached doesn’t sound like your routine software error message, right? Well, it’s not an error message.

As you’ve seen in this post, a detached HEAD doesn’t mean something is wrong with your repo. Detached HEAD is just a less usual state your repository can be in. Aside from not being an error, it can actually be quite useful, allowing you to run experiments that you can then choose to keep or discard.

Carlos Schults is a .NET software developer with experience in both desktop and web development, and he’s now trying his hand at mobile. He has a passion for writing clean and concise code, and he’s interested in practices that help you improve app health, such as code review, automated testing, and continuous build.

В этом уроке мы с вами узнаем, как перемещать указатель HEAD. Это знание откроет перед нами много возможностей. Например, мы сможем откатиться к предыдущему коммиту, добавить в созданный ранее коммит файлы или исправить ошибку в уже сделанном коммите.

Git. Урок 4.
Перемещение курсора и отмена изменений. Команды git restore, git rm, git reset, git checkout, git commit, git revert.

В этом уроке мы с вами узнаем, как перемещать указатель HEAD. Это знание откроет перед нами много возможностей. Например, мы сможем откатиться к предыдущему коммиту, добавить в созданный ранее коммит файлы или исправить ошибку в уже сделанном коммите.

Smartiqa Git cover

  • Команды: git restore, git rm, git reset, git checkout, git commit, git revert.

Оглавление

Теоретический блок

1. Удаляем и восстанавливаем файлы правильно. Команды git rm и git restore.
2. Просмотр старых коммитов и перемещение указателя HEAD

  1. История выводится не полностью
  2. Как переключиться обратно
  3. Зачем может понадобиться переключать указатель на старые коммиты, и важные особенности состояния “detached head”.

3. Откат коммитов. Команда git revert.
4. Удаление и объединение коммитов. Команда git reset.

  1. Редактирование или отмена последнего коммита
  2. Объединение нескольких коммитов в один.
  3. Удаление последних нескольких коммитов.
  4. Отмена изменений команды git reset.

5. Различие команд git reset и git checkout и git revert.

Перейти

Практический блок

1. Задание
2. Решение

Перейти

ТЕОРЕТИЧЕСКИЙ БЛОК

Сегодня мы познакомимся с командами, которые позволят удалять и восстанавливать файлы в рабочей копии и индексе, отменять изменения на уровне целых коммитов, возвращаться в прошлое нашего репозитория и манипулировать указателем HEAD.

Удаляем и восстанавливаем файлы правильно. Команды git rm и git restore.

Начнем с удаления файлов. У читателя может возникнуть вопрос: почему нельзя просто взять и переместить файл из репозитория в корзину, как мы привыкли это делать с обычными файлами? Давайте разбираться.

Как мы говорили во втором уроке, файл в репозитории может находится в нескольких состояниях:

1. Отслеживаемый.

  1. Измененный (неподготовленный к коммиту).
  2. Подготовленный к коммиту.

2. Неотслеживаемый.

Когда мы делаем файл отслеживаемым или подготавливаем его к коммиту, информация об этом файле записывается в индекс (файл .git/index). Эта информация никуда не денется, даже если мы удалили файл из рабочей копии. Рассмотрим пример. Допустим, мы выполним следующие действия:

1. Создадим файл sample.txt и внесем в него какие-то изменения;
2. Сделаем его отслеживаемым;
3. Удалим файл sample.txt;
4. Сделаем коммит;

В таком случае, просматривая содержимое нового коммита, мы обнаружим, что файл sample.txt в нем присутствует, несмотря на то, что мы вроде бы его удалили. Это произошло из-за того, что хоть мы и удалили файл из рабочей копии, но не удалили информацию о нем из индекса.

В Git существует специальная команда, чтобы удалять файлы “правильно”.

Команда git rm

Формат

git rm <ключ> <имя файла>

Ключи

-f, –forced
Форсированное удаление. Файл будет удален, несмотря на все предупреждения. Используйте этот ключ осторожно.

–cached
С этим ключом команда очистит только информацию о файле в индексе. Сам файл в рабочей копии останется нетронутым, независимо от того, изменен он или нет. При этом файл из области “подготовленный к коммиту” (англ. staged) перейдет в область “неотслеживаемый” (англ. untracked).

Что делает

Удаляет файл из рабочей копии и индекса / только из индекса. Данная команда не может удалить файл только из рабочей копии.

Пример

# Удалим файл sample.txt из рабочей копии и индекса
$ git rm sample.txt

# Удалим файл sample.txt из индекса и перемеcтим его в категорию Untracked
$ git rm –cached sample.txt

Внимательный читатель спросит: но почему нельзя удалить файл из репозитория обычным образом, а затем добавить изменения в индекс командой git add и сделать коммит? На самом деле можно. Команда git rm является сокращением вышеописанных действий. Ее применение считается более правильным, поскольку она короче и красивее. Давайте рассмотрим небольшой пример:


# Удалим файл обычным образом
$ rm sample.txt
$ git status
On branch main
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        deleted:    sample.txt
$ git add -A

# И вся последовательность команд выше эквивалентна всего одной:
$ git rm sample.txt
rm 'sample.txt'

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        deleted:    sample.txt

# Осталось только сделать коммит, чтобы сохранить изменения:
$ git commit -m "L-04: Deleted sample.txt"
[main f34705a] L-04: Deleted sample.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 sample.txt

Обычное удаление файла, который отслеживается Git

Обычное удаление файла, который отслеживается Git

”Правильное” удаление файла. Команда git rm

“Правильное” удаление файла. Команда git rm.

Итак, мы разобрались с удалением файлов. Но что, если мы захотим восстановить файл после удаления или изменения в рабочей копии или индексе? Для этого существует команда git restore. Давайте разберем ее подробнее.

Команда git restore

Формат

git restore <ключ> <имя файла>

Ключи

-s, –source=<tree>
Этот ключ нужен, чтобы передать команде путь к коммиту (ветке, пользовательскому указателю), откуда мы будем восстанавливать файл. По умолчанию файл берется из области индекса.

worktree (англ. рабочая копия)
–staged (англ. область индекса)

Эти два ключа позволяют указать, где именно восстанавливать файл. По умолчанию, если ни один из этих двух ключей не передан, файл восстанавливается только в рабочей копии.

Если же передан ключ –staged, файл восстанавливается только в области индекса. В этом случае источником восстановления по умолчанию является коммит, на который указывает HEAD (поскольку мы не можем восстановить файл в области индекса из самой же области индекса).

Если же вы хотите восстановить файл и в рабочей копии, и в области индекса, вам нужно передать оба ключа.

Что делает

Восстанавливает указанный файл из переданного источника. По умолчанию источником является индекс. Если файла нет в указанном источнике, файл будет удален.

Пример

# Если вы случайно удалили файл sample.txt обычным способом, то можно восстановить его из индекса
$ git restore sample.txt

# Вернем файл sample.txt к определенному коммиту с хэшем 09c2240. При этом мы изменим только файл в рабочей копии, файл в области индекса не поменяется.
$ git restore –source 09c2240 sample.txt

# Вернем файл sample.txt в индексе к состоянию последнего коммита (отменим все внесенные изменения или удалим файл, если в предыдущем коммите его не было), при этом изменения коснутся только индекса файла, рабочая копия не поменяется.
$ git restore –staged sample.txt

# Сделаем то же, что и в предыдущем примере, но теперь изменения затронут и файл в рабочей копии.
$ git restore –staged –worktree sample.txt

С помощью команды git restore можно сделать неотслеживаемыми файлы, которые вы случайно добавили командой git add. Приведем пример:


# Допустим у нас есть некоторый репозиторий, просмотрим статус файлов:
$ git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
       file_to_commit.txt
       another_file_to_commit.txt
       file_not_for_commit.txt

# Для простоты добавим все файлы в индекс разом
$ git add -A

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   file_to_commit.txt
        new file:   another_file_to_commit.txt
        new file:   file_not_for_commit.txt

# А теперь восстановим файл “file_not_for_commit.txt” в области индекса. Тогда источником станет последний коммит, а в нем такого файла нет (файл же новый). Поэтому файл будет удален из области индекса. Кстати, можно заметить, что даже Git подсказывает нам: “use "git restore --staged <file>..." to unstage”. 

$ git restore --staged file_not_for_commit.txt

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   another_file_to_commit.txt
        new file:   file_to_commit.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        file_not_for_commit.txt

# Для справки: в данном случае эквивалентной командой будет:
$ git rm --cached file_not_for_commit.txt
rm 'file_not_for_commit.txt'

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   another_file_to_commit.txt
        new file:   file_to_commit.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        file_not_for_commit.txt

Подведем итогИтак, мы теперь умеем удалять и восстанавливать файлы из индекса и рабочей копии.
1. Чтобы удалить файл правильно, воспользуйтесь командой git rm. Она удалит файл из индекса и из рабочей копии.

  1. Вариант git rm –cached удалит файл из индекса и переместит его в категорию Untracked.
  2. По своей сути git rm <filename> представляет сокращение двух команд: rm <filename> и git add <filename>.

2.Чтобы восстановить файл в рабочей копии и/или индексе, воспользуйтесь командой git restore.

  1. Ключ –source=<tree> позволит вам указать место, откуда брать файл для восстановления. По умолчанию этим местом является область индекса.
  2. По умолчанию файл восстанавливается в рабочей копии, но вы можете восстановить файл в области индекса с ключом –staged или одновременно в области индекса и в рабочей копии, передав ключи –staged –worktree. В этом случае по умолчанию (если вы не передали ключ –source) файл будет взят из коммита, на который указывает HEAD.

Просмотр старых коммитов и перемещение указателя HEAD

Итак, пришло время разобраться, каким образом можно перемещать HEAD (и другие указатели тоже) и что это дает. Для начала нужно прояснить несколько важных аспектов, которые мы уже упоминали, но не обращали ваше внимание на них:

1. Весь репозиторий – это древовидный граф, ноды которого – наши коммиты, а ребра – родительские отношения между коммитами.
2. HEAD – это указатель (то есть ссылка на один из коммитов), главное назначение которого – определять, в каком состоянии находится рабочая копия. На какой коммит указывает HEAD – в таком состоянии файлы и находятся в рабочей области.
3. Обычно HEAD указывает не на определенный коммит, а на указатель ветки, который в свою очередь указывает на конкретный коммит.
4. HEAD можно перемещать: при перемещении указателя файлы в рабочей копии изменятся так, чтобы соответствовать коммиту, на который указывает HEAD.
5. Указывая путь до чего бы то ни было, вы можете использовать как абсолютные указатели, например, хэш коммита или имя ветки, так и относительные. Вспомним, как пользоваться относительными указателями:

  1. Знак ^ означает “предыдущий”. Например путь HEAD^ означает “предыдущий коммит перед тем, на который указывает HEAD
  2. Знак ~ позволяет вам указать число коммитов. Например, запись HEAD~7 означает “7 коммитов назад от коммита, на который указывает HEAD“.

Использование относительных путей особенно удобно, поскольку они позволяют не запоминать хэши коммитов и не обращаться к истории репозитория чаще, чем это действительно нужно.

Давайте подробнее разберемся с перемещением указателя, а затем поговорим, зачем это может быть нужно. Итак, чтобы перемещать указатель, нужно воспользоваться знакомой нам из прошлого урока командой git checkout.

Команда git checkout

Что делает

Переводит курсор HEAD на указанный коммит или другой указатель и копирует.

Пример

# Передвинем HEAD на два коммита назад
$ git checkout HEAD~2
HEAD is now at 7194f7e L-04: Fixing gradient bug

Можно заметить, что синтаксис совершенно такой же, как и в переключении ветки из предыдущего урока. Все верно, ведь переключение ветки – ни что иное, как передвижение указателя HEAD с указателя одной ветки на указатель другой. Чтобы подробно разобраться, что происходит при передвижении указателя, давайте рассмотрим пример. Изначально у нас есть такой репозиторий.

Исходный репозиторий

Серыми овалами обозначены коммиты, текст на них – часть хэша соответствующего коммита. Коричневым прямоугольником обозначен указатель ветки: она у нас одна и называется main. Белым прямоугольником обозначен указатель HEAD, иногда его называют курсором. Его-то мы и будем двигать. Давайте попробуем сдвинуть HEAD на два коммита назад (то есть на коммит с хэшем 90ab…) и посмотрим, что будет.


$ git checkout HEAD~2
Note: switching to 'HEAD~2'.

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 switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 90abf7e L-04: Fixing gradient bug

Предупреждение Git мы разберем чуть ниже, а сейчас давайте посмотрим, в каком состоянии мы оказались.

Граф Git после перевода указателя HEAD на два коммита назад

Граф Git после перевода указателя HEAD на два коммита назад

Как видно из рисунка, указатель HEAD сейчас действительно передвинут на два коммита назад. Но указатель ветки main остался на месте и все еще указывает на последний коммит. Такое состояние, когда HEAD указывает не на указатель ветки, а непосредственно на сам коммит, называется detached head.

Detached head (англ. дословно – отрубленная голова, имеется в виду указатель HEAD, отключенный от графа) – состояние, при котором указатель HEAD не указывает ни на одну из веток репозитория, а ссылается непосредственно на сам коммит.

Если прочесть предупреждение Git выше, можно заметить, что он сообщает нам, что мы оказались в состоянии detached head. Давайте рассмотрим несколько особенностей этого состояния:

2.1. История выводится не полностью

Действительно, если мы из текущего состояния выполним команду git log:


$ git log --pretty=oneline
90abf7ef211229adfa4cb75e0f35a0561dd15467 (HEAD)  L-04: Fixing gradient bug
3a3bb706651a19013822c09e5c70c9fc425a66dc L-04: Adding gradient function
d469222a7a760daa3cd56747e216e3de2a3343ee L-04: Initial commit

Видно, что вывелись только те коммиты, которые были сделаны позже коммита, на котором сейчас стоит HEAD. Если же мы хотим просмотреть всю историю, нужно воспользоваться ключом –all:


$ git log --all --pretty=oneline
62aa1ffe0587c7ffa3d865a3233da04d65818030 (main) L-04: Adding autograd
33ff7207fed9cbb34c9f3334249ef0707477f278 L-04: Adding neuron class
90abf7ef211229adfa4cb75e0f35a0561dd15467 (HEAD)  L-04: Fixing gradient bug
3a3bb706651a19013822c09e5c70c9fc425a66dc L-04: Adding gradient function
d469222a7a760daa3cd56747e216e3de2a3343ee L-04: Initial commit

Тогда нам видна полная история репозитория. Если присмотреться, то можно увидеть, что напротив первого сверху коммита в скобках написано main. Так Git сообщает нам, что на данный коммит указывает ветка main. Аналогично с третьим сверху коммитом: напротив него написано HEAD. Это означает, что HEAD указывает на этот коммит.

2.2. Как переключиться обратно

Вам вовсе необязательно смотреть полную историю каждый раз, когда вы хотите вернуться обратно на ветку main. В предыдущем уроке мы рассматривали команду git checkout – и говорили, что с ее помощью можно вернуться на ветку, с которой мы переключились на текущую. Так вот, эта команда работает не только для веток, но и для любых перемещений HEAD. Если мы хотим перенести HEAD обратно, достаточно выполнить:


$ git checkout -
Previous HEAD position was 90abf7e L-04: Fixing gradient bug
Switched to branch 'main'

Как видно по выводу Git, мы успешно вернулись обратно на ветку main и вышли из состояния detached head.

У данного способа есть один минус: если вы “прыгали” по коммитам репозитория несколько раз, то git checkout – вернет вас на последний коммит, на котором вы были, а не на ветку main. В данном случае поможет простое и гениальное:


$ git checkout main
Previous HEAD position was 90abf7e L-04: Fixing gradient bug
Switched to branch 'main'

То есть мы напрямую указываем Git, что хотим переключиться на ветку main (или любую другую), таким образом выходя из состояния detached head.

2.3. Зачем может понадобиться переключать указатель на старые коммиты, и важные особенности состояния “detached head”.

На самом деле, все зависит от конкретной задачи и проекта, над которым вы работаете. Вообще состояние detached head на практике используется довольно-таки редко. Но все же можно привести несколько примеров использования этого состояния:

Пример 1
Самый банальный пример – просмотр файлов в определенном коммите. Допустим, вы хотите просмотреть содержимое файла main.py в коммите с определенным хэшем.
Просто выполните:


# Команда cat выводит в консоль содержимое файла.
$ git checkout 5df3f7e
$ cat main.py
print(“Hello world”)

Таким образом, вы можете просмотреть содержимое любого, когда либо закоммиченного файла, что бывает довольно удобно.

Пример 2
Кроме того, когда вы работаете над большим проектом, может быть нужно вернуться на несколько коммитов назад и создать свою ветку оттуда: например, чтобы протестировать экспериментальную функцию в том состоянии проекта. Сделать это можно так:


# Переключим указатель HEAD на определенный коммит:
$ git checkout 9a4e88b

# Теперь создадим новую ветку
$ git checkout -b feature
Switched to a new branch 'feature'

# Добавим файл и сделаем коммит
$ echo "just docs we forgot to put" > docs.md
$ cat docs.md
just docs we forgot to put

$ git add -A
$ git commit -m "L-04: Adding docs"
[feature d5e3273] L-04: Adding docs
 1 file changed, 1 insertion(+)
 create mode 100644 docs.md

Теперь у нас есть целая ветка feature, которая берет свое начало с коммита 9a4e88b. На ней мы можем проводит эксперименты с тем состоянием репозитория, которое было несколько коммитов назад, не боясь навредить остальной части репозитория.

Заметьте, что когда мы создали и переключились на ветку, мы вышли из состояния detached head. Если бы мы сделали коммит, не создавая для этого ветки, этот коммит оказался бы “оторванным” от истории: на него не ссылалась бы ни одна ветка, а в выводе истории (даже полной) его не было бы видно. Иначе говоря, если бы мы сделали коммит без ветки, мы бы смогли получить к нему доступ только запомнив его хэш наизусть.

Поэтому с состоянием detached head следует быть очень осторожным: если забыть создать новую ветку и начать делать коммиты, их можно с легкостью потерять.

На самом деле, если вы сделали несколько коммитов из состояния detached head и забыли создать ветку, ничего страшного: ветку можно создать прямо сейчас. Рассмотрим пример с тем же репозиторием:


# Переключимся в состояние “detached head” на ветке main.
$ git checkout d5e3273

# Теперь мы создадим несколько коммитов, исключительно для примера:
$ echo 'print('hello world')' > hello_world.py
$ echo 'print('Hi')' > say_hi.py
$ echo 'Some text file' > about.txt
$ git add hello_world.py
$ git commit -m "L-04: Adding hello_world.py"
[detached HEAD d013c75] L-04: Adding hello_world.py
 1 file changed, 1 insertion(+)
 create mode 100644 hello_world.py

$ git add say_hi.py
$ git commit -m "L-04: Adding say_hi.py"
[detached HEAD 58f8c22] L-04: Adding say_hi.py
 1 file changed, 1 insertion(+)
 create mode 100644 say_hi.py

$ git add about.txt
$ git commit -m "adding about.txt"
[detached HEAD 7c10724] L-04: Adding about.txt
 1 file changed, 1 insertion(+)
 create mode 100644 about.txt

# А сейчас мы вспомнили, что сделали несколько коммитов в состоянии “detached head” и испугались, что потеряем их. Но не стоит бояться, можно просто создать ветку прямо сейчас:
$ git checkout -b feature
Switched to a new branch 'feature'

# И чтобы убедиться, что мы не потеряли ни одного коммита, давайте взглянем на историю ветки feature
$ git log --pretty=oneline main..feature
7c10724b12dba69ed1acf4c6fef804c251f7c290 (HEAD -> feature) L-04: Adding about.txt
58f8c226952d500df7a9c2f798011ab20165a286 L-04: Adding say_hi.py
d013c75418ac71ca8f4578583aa23d7567dab332 L-04: Adding hello_world.py
d5e327368d12d6e5ef5b04e16af1d96319069805 L-04: Adding docs

Даже в состоянии detached head коммиты сохраняют родительские отношения, то есть каждый коммит знает, кто его предшественник. Поэтому мы в любой момент можем восстановить их последовательность, если найдем крайний коммит в данной цепочке. То есть для нас главное не забыть создать ветку, находясь в состоянии detached head. А вот создать ее можно в любой момент: до того, как мы сделаем первый коммит из этого состояния, или в любой другой момент – не важно, главное сделать это до переключения на другую ветку или коммит. Тогда у нас будет ссылка на крайний коммит ветки, а по его родителям мы сможем определить всю последовательность.

Подведем итог

  1. Указатель HEAD можно перемещать на разные коммиты, точно так же, как мы перемещали его между ветками. Для этого нужно использовать команду git checkout.
  2. Когда мы перемещаем HEAD с указателя ветки на коммит, мы попадаем в состояние detached head.
  3. В состоянии detached head стоит быть осторожным: если планируете делать коммиты, лучше сразу создайте ветку командой git checkout -b <имя ветки>.
  4. Если вы сделали несколько коммитов из состояния detached head, забыв создать ветку – ничего страшного, ветку можно создать в любой момент.
  5. Обычно detached head используется редко, но вы можете использовать его, чтобы просматривать старые версии файлов или экспериментировать с предыдущими версиями проекта.

    Откат коммитов. Команда git revert.

    Пожалуй одна из самых важных частей в изучении Git – научиться откатываться к предыдущим коммитам. Смысл отката мы обсуждали в предыдущих уроках: ваш проект может перестать работать по непонятным вам причинам после внесения некоторых изменений в код, в таком случае важно быстро вернуть все к рабочему состоянию и только потом заниматься поиском ошибки. В этом-то случае нам и поможет откат коммитов и команда git revert.

    Суть работы данной команды в том, что она создает новый коммит, который отменяет изменения внесенные в переданном коммите (последовательности коммитов).

    Команда git revert

    Формат

    git revert <ключи> <адрес коммита>

    Ключи

    -n
    Не делает коммит. С данным ключом изменения коснутся только рабочей копии.

    –abort

    Данный ключ используется только при разрешении конфликтов.
    Прерывает выполнение команды после разрешения конфликтов.

    –continue
    Данный ключ используется только при разрешении конфликтов.
    Продолжает выполнение команды после разрешения конфликтов.

    Что делает

    Отменяет изменения, внесенные в переданном коммите.

    Пример

    # Отменим изменения, внесенные 2 коммита назад
    $ git revert HEAD~2

    # Отменим все изменения в коммитах, начиная с пятого с конца и заканчивая вторым с конца.
    $ git revert HEAD~5..HEAD~2

    Команда git revert очень важна, поэтому давайте разберем, как с ней работать. Рассмотрим самый простой пример, в котором у нас не возникнет файловых конфликтов. Допустим, мы хотим отменить изменения предпоследнего коммита. Главное, что здесь нужно запомнить – это что у нас не должно быть незакоммиченых изменений в рабочей директории, ведь мы все-таки делаем реверт-коммит. Если у вас есть таковые, лучшим решением станет закоммитить (или удалить) все изменения, и только потом делать реверт.

    
    # Выполним реверт предпоследнего коммита. Кстати, такой реверт никогда не вызовет конфликтов.
    
    $ git revert HEAD~1
    
    # Как только мы выполним команду, будет открыт консольный редактор, чтобы вы могли отредактировать сообщение нового коммита. Можно что-то дописать, можно сразу закрыть редактор сочетанием Ctrl+X (здесь приведено сочетание для редактора “nano”). Решать вам. В нашем случае окно редактора будет выглядеть так.
    
    Revert "L-04: Addit docs.txt"
    
    This reverts commit aadfbc3a6756289727e56ac3de59004e66e40033.
    
    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    #
    # On branch master
    # Your branch is up to date with 'origin/master'.
    #
    # Changes to be committed:
    #       modified:   docs.txt
    #
    
    
    
    ^G Get Help  ^O Write Out ^W Where Is  ^K Cut Text  ^J Justify   ^C Cur Pos
    ^X Exit      ^R Read File ^ Replace   ^U Paste Text^T To Spell  ^_ Go To Line
    
    # После закрытия редактора, в терминал будет выведена информация об успешном реверте коммита
    
    [main e933971] Revert "addit docs.txt"
     1 file changed, 1 insertion(+), 1 deletion(-)
    

    Итак, это был самый простой пример. В основном сложности git revert связаны с разрешением файловых конфликтов. Разрешать мы их научимся в следующем уроке, а пока давайте обсудим, из-за чего может возникнуть конфликт. Может, обладая этими знаниями и столкнувшись с конфликтом во время выполнения git revert,вы поймете, что вам совсем не нужен откат.

    Иногда при отмене изменений может возникнуть ситуация, когда в файл в рабочей копии были внесены изменения с момента коммита находится не в том же состоянии, как в коммите, который мы отменяем. Звучит сложно, поэтому давайте приведем пример. Допустим, у нас есть следующий репозиторий.

    Репозиторий: 4 коммита - 4 изменения в файле docs.md

    Репозиторий: 4 коммита – 4 изменения в файле docs.md

    В каждом из вышеуказанных коммитов всего один файл: docs.md. Над хэшем каждого коммита на рисунке указано содержимое docs.md, которое было внесено в данный коммит.

    Теперь, когда перед глазами у вас есть диаграмма, давайте разберемся, в каком случае при откате возникнет конфликт файлов. На самом деле, конфликт возникнет при откате любого, кроме самого последнего коммита. Как мы уже говорили, при реверте коммита, его изменения отменяются, то есть файлы возвращаются к состоянию предыдущего коммита. Например, если мы попробуем откатить коммит 33ff, то содержимое файла docs.md изменится с

    Содержимое docs.md, коммит 33ff

    Содержимое docs.md, коммит 90ab

    Но ведь в последнем коммите, т.е. в рабочей копии находится файл, содержимое которого

    Содержимое docs.md, коммит c732f69

    
    Some docs here
    and here
    also here
    
    

    что совсем не соответствует содержимому коммита, который мы откатываем. То есть с момента того коммита мы уже успели изменить файл docs.md, и теперь Git не очень понимает, как именно делать откат:

    1. оставить файл в рабочей копии нетронутым или,
    2. наоборот, заменить файл в рабочей копии на файл после отката.

    Поэтому и возникает конфликт. Git напрямую спрашивает у нас: “Какой файл мне оставить?”. На практике это выглядит вот так:

    
    # Пробуем откатить коммит .
    $ git revert 33ff381
    Auto-merging docs.md
    CONFLICT (content): Merge conflict in docs.md
    error: could not revert 33ff381... L-04: Adding info to docs
    hint: after resolving the conflicts, mark the corrected paths
    hint: with 'git add <paths>' or 'git rm <paths>'
    hint: and commit the result with 'git commit'
    

    В следующем уроке, когда мы будем говорить о слиянии веток, мы научимся разрешать файловые конфликты.

    Кстати, если вы попали в ситуацию, когда Git сообщил вам о конфликте файлов, а вы такого не ожидали, или вы просто передумали делать откат из-за конфликта, можете в любой момент выполнить git revert –abort. Это остановит откат, и вы сможете спокойно разобраться, откуда возникает конфликт.

    Подведем итог

    1. git revert – команда, отменяющая изменения переданного коммита. Она заменяет файлы в рабочей копии на файлы предка переданного коммита, а затем делает коммит, чтобы сохранить изменения.
    2. Иногда во время отката возникают конфликты. Их не стоит бояться, но нужно быть внимательным и разобраться, откуда возникает конфликт.
    3. Если вы столкнулись с конфликтом во время отката и передумали продолжать git revert, выполните команду git revert –abort. Она вернет все как было и отменит откат.
    4. В сообщении реверт-коммита следует указывать полезную информацию: зачем вы сделали откат, каким образом вы сливали файлы, если были конфликты, и т.д.

    Удаление и объединение коммитов. Команда git reset.

    Помните, мы говорили про команду git checkout и перемещение указателя HEAD? Тогда мы перемещали только сам указатель HEAD и попадали в состояние detached head. Команда git reset позволяет нам перемещать указатель ветки вместе с указателем HEAD. Давайте разберем эту команду подробнее.

      Команда git reset

      Формат

      git reset <ключи> <адрес коммита>

      Ключи

      –soft
      С этим ключом, команда не отменяет изменения ни в индексе, ни в рабочей копии. Все ваши файлы останутся в том же состоянии, в котором были, но указатель ветки будет передвинут.

      –hard
      С этим ключом команда удалит все изменения так, чтобы состояние индекса и рабочей копии полностью соответствовали коммиту, к которому мы сделали reset

      Что делает

      Переносит указатель ветки на переданный коммит.

      Пример

      # Сделаем reset последнего коммита, который мы сделали по ошибке. При этом оставим файлы в том же состоянии.
      $ git reset –soft HEAD^

      # Отменим последние три коммита и удалим все изменения в файлах.
      $ git reset –hard HEAD~3
      HEAD is now at 2f96b73 L-04: Create main.py

      Чтобы лучше понять смысл этой команды приведем граф репозитория. Изначально он выглядел так.

      Репозиторий до ресета

      Теперь выполним “мягкий” reset последних двух коммитов.

      
      $ git reset --soft HEAD~2
      
      

      Теперь наш граф выглядит так.

      Репозиторий после выполнения команды git reset

      Репозиторий после выполнения команды git reset

      Причем файлы в рабочей копии остались в том же состоянии, что и были в коммите 62aa.

      Как видно из рисунка, Git создал новый указатель – ORIG_HEAD. Этот указатель создается при выполнении команды git reset и ссылается на тот коммит, от которого мы делали reset. Как мы увидим ниже, это очень полезный в работе с git reset указатель.

      Кстати, если мы сейчас сделаем коммит:

      
      $ git add -A
      $ git commit -m “L-04: Reset commit”
      [main 03953f8] L-04: Reset commit
       1 file changed, 3 insertions(+), 1 deletion(-)
      

      То получим такую картину:

      Новый коммит после ресета

      Новый коммит после ресета

      То есть наш коммит пойдет по ответвлению от исходной ветки main. Поэтому будьте внимательны, создавая новые коммиты.

      Итак, теперь поговорим, как на практике используется git reset. В основном, все использование сводится к трем пунктам:

      1. Редактирование/отмена последнего коммита.
      2. Объединение нескольких коммитов в один.
      3. Удаление коммитов.
      4. Отмена изменений команды git reset.

      Давайте подробно разберем каждый пункт.

      4.1. Редактирование или отмена последнего коммита

      Если вы случайно опечатались в сообщении последнего коммита, или хотите добавить в него файлов, то git reset поможет вам. Но прежде всего нам нужно разобрать новые ключи уже знакомой команды git commit. Они потребуются нам в дальнейшем.

      Команда git commit

      Формат

      git commit <ключи> <адрес переданного коммита>

      Ключи


      С этим ключом, команда, создавая новый коммит, возьмет сообщение и информацию об авторе из переданного коммита.


      С этим ключом, команда, создавая новый коммит, возьмет сообщение и информацию об авторе из переданного коммита, а затем откроет редактор, чтобы вы могли отредактировать сообщение коммита.

      –amend
      С этим ключом коммит будет объединен с последним коммитом на текущей ветке. Этот ключ используется для редактирования содержимого и сообщения последнего коммита.

      Что делает

      Создает новый коммит.

      Пример

      # Сделаем новый коммит с таким же сообщением и информацией об авторе, как у коммита, на который указывает ветка feature.
      $ git commit -C feature

      # Сделаем то же самое, но отредактируем сообщение коммита.
      $ git commit -с feature

      Теперь, когда мы познакомились с новыми ключами, можем перейти к редактированию последнего коммита. Последовательность действий в данной ситуации такая:

      1. Откатиться к предпоследнему коммиту командой git reset –soft HEAD^
      2. Добавить в коммит новые файлы, если вам это нужно, использовав команду git add.
      3. Выполнить git commit -c ORIG_HEAD, если вы хотите отредактировать сообщение, или git commit -C ORIG_HEAD, если вы хотите оставить сообщение коммита без изменений.

      Полезно знать
      Кстати, данная последовательность команд идентична git commit –amend. Чтобы отредактировать последний коммит, следуйте инструкции:
      1. Если вы хотите добавить в коммит файлы, то для начала добавьте их в индекс командой git add <имя файла>. Если не хотите, то проверьте, что проиндексированных изменений нет, иначе они будут добавлены в последний коммит.
      2. Выполните

      1. git commit –amend. Тогда откроется консольный редактор, где вы сможете отредактировать сообщение последнего коммита.
      2. git commit –amend -m “<новое сообщение>”. Тогда сообщение последнего коммита будет заменено на <новое сообщение>.

      В случае, если же вы хотите насовсем удалить последний коммит, просто выполните

      
      $ git reset --hard HEAD^
      HEAD is now at c732f69 L-04: Adding more info to docs
      

      Таким образом, последний коммит будет удален и не будет отображаться в логе. Тем не менее, вернуться к нему вы сможете по указателю ORIG_HEAD, который оставит Git.

      4.2. Объединение нескольких коммитов в один.

      Иногда на практике возникают ситуации, когда для удобства восприятия и красивой истории необходимо объединить несколько последних коммитов в один. Порядок действий тут почти такой же, как и в случае редактирования последнего коммита:

      1. Выполните git reset –soft HEAD~n, где n это число коммитов, которые вы хотите объединить. Эта команда вернет указатель ветки на n коммитов назад, оставив изменения в индексе и рабочей копии нетронутыми. То есть после выполнения этой команды вы откатитесь на n коммитов назад, но все изменения внесенные этими коммитами останутся у вас в рабочей копии и индексе.
      2. Выполните git commit -c ORIG_HEAD, а затем отредактируйте сообщение коммита должным образом. Эта команда сделает коммит всех изменений в индексе. То есть она сделает коммит, который по своему содержимому представляет объединение последних n коммитов.

      Собственно, это все. То есть на самом деле мы просто откатили последние несколько коммитов, а затем создали новый коммит, который собирает в себе все их изменения. При этом изначальные коммиты никуда не делись. Их не будет видно в истории, но если вы помните их хэш, то в любой момент сможете переключиться на любой из них с помощью команды git checkout.

      4.3. Удаление последних нескольких коммитов.

      Пожалуй, самая простая ситуация. Чтобы удалить последние n коммитов, выполните

      
      $ git reset --hard HEAD~n
      
      

      Где n – это, конечно, какое-то конкретное число. После этого удаленные коммиты не будут выводиться в истории (совсем, даже с флагом –all).

      4.4. Отмена изменений команды git reset.

      Если вы каким-то образом случайно выполнили git reset и решили все вернуть, просто переместите указатель ветки обратно, использовав команду

      Эта команда вернет указатель ветки на коммит, с которого вы делали git reset, и вы вернете все изменения, даже если использовали ключ –hard.

      Подведем итог
      Итак, мы выучили новую команду git reset. Она используется, чтобы перемещать указатель ветки по графу Git. На практике ее в основном применяют для

      1. Редактирования или удаления последнего сделанного коммита.
      2. Объединения последних нескольких коммитов в один.
      3. Удаления последних нескольких коммитов.

      После использования git reset, Git создаёт указатель ORIG_HEAD, который ссылается на коммит, с которого мы сделали reset.

      Не стоит забывать, что даже если вы использовали git reset –hard, вы всегда можете вернуть все к изначальному состоянию, выполнив git reset ORIG_HEAD. Это вернет указатель ветки на коммит, с которого вы делали reset.

      Различие команд git reset и git checkout и git revert

      Сегодня мы изучили несколько команд, которые на первый взгляд очень похожи друг на друга. Давайте разберемся, в чем разница между ними. Для начала приведем краткую справку, чтобы напомнить, что делает каждая из команд.

      Изученные команды

      Команда git revert

      Данная команда создает новый коммит, который отменяет действие одного из предыдущих коммитов. То есть новый коммит появится в истории, а предыдущие коммиты не изменятся.

      Команда git reset

      Работа этой команды зависит от вызова. Если вызвать git reset <ссылка>, то команда переместит ветку, на которую указывает HEAD, на переданную ссылку. Затем, в зависимости от опций –soft/–hard/–mixed, команда:

      1. либо останавливается,
      2. либо обновляет индекс и рабочую копию так, чтобы они соответствовали текущему коммиту,
      3. либо обновляет только индекс соответственно.

      То есть она изменит историю, исключив из нее все коммиты, оставшиеся после HEAD. Говоря проще, команда действует так:

      1. Перемещает ветку, на которую указывает HEAD, или только HEAD (если он находится в состоянии detached HEAD). Останавливается на этом шаге, если передан ключ –soft.
      2. Делает индекс таким же, как в коммите, на который указывает HEAD. Останавливается на этом шаге, если не передан ключ –hard.
      3. Делает рабочую копию такой же, как коммит, на который указывает HEAD

      Вы также можете вызвать основную команду (без ключей –hard и –soft), передав ей путь к файлу: git reset <ссылка> <имя файла>. В этом случае команда будет действовать также, но эффект будет отличаться, поэтому этот случай стоит рассмотреть отдельно.

      В случае, если вы не передали ссылку, вместо нее будет подставлен HEAD. То есть она обновит индекс так, чтобы он соответствовал коммиту, на который указывает HEAD. Иначе говоря, скопирует файл из HEAD в индекс. То есть ее действие противоположно git add <имя файла>: она отменит добавление файла в индекс.

      Если же вы указали ссылку, то в вашем индексе окажется файл из одного из предыдущих коммитов. То есть в рабочей копии файл не изменится, но если вы сразу же сделаете коммит, то в новом коммите будет файл из одного из предыдущих коммитов.

      Команда git checkout

      Действие этой команды тоже зависит от вызова.

      1. Если вызвать git checkout <ссылка>, то команда либо перемещает указатель HEAD на переданную ссылку (т.е. на другую ветку или коммит). Историю данная команда не меняет, если только не забыть использовать ключ –all: git log –all.
      2. Если выполнить git checkout <ссылка> <имя файла>, то команда скопирует содержимое файла из переданного коммита в рабочую копию и индекс.

      Команда git restore

      Копирует файл из переданной ссылки в рабочую копию, индекс или сразу и туда, и туда. Изначально эта команда появилась, как аналог git checkout <ссылка> <имя файла>, но ее функционал немного шире. Данная команда также не меняет историю.

      Между git checkout, git reset и git restore есть несколько различий:

      1. Команда git checkout <ссылка> похожа на git reset –hard <ссылка>: в обоих случаях перемещается HEAD и меняется рабочая копия. Однако есть и разница:.

      1. Первое отличие состоит в том, что git checkout перемещает только HEAD, в то время как git reset перемещает HEAD и ветку, на которую указывает HEAD.
      2. Еще одно отличие заключается в том, что git checkout проверяет, что у вас в рабочей копии нет измененных файлов, в то время как git reset просто заменяет все файлы без разбора.

      В случае, когда мы находимся в состоянии detached head и у нас нет измененных файлов, эти две команды идентичны.

      2. Команда git checkout <ссылка> <имя файла> также имеет общие черты с git reset <ссылка> <имя файла>. Отличие состоит в следующем:

      git checkout <ссылка> <имя файла> изменяет и рабочую копию, и индекс, в то время, как git reset <ссылка> <имя файла> меняет только индекс. Кстати, идентичной командой для git checkout <ссылка> <имя файла> была бы команда git reset –hard <ссылка> <имя файла>, если бы ее можно было так использовать: это очень небезопасно для рабочей копии.

      3. Команда git restore задумывалась как аналог git checkout <ссылка> <имя файла>. Тем не менее, между ними есть следующие отличия:

      1. Используя git checkout <ссылка> <имя файла>, в качестве ссылки вы можете передать только определенный коммит, в то время как git restore может скопировать файл в рабочую копию прямо из индекса
      2. Команда git checkout всегда копирует файлы одновременно и в рабочую копию, и в область индекса, что не всегда удобно. В свою очередь, git restore может принять ключи –staged и –worktree, определяющие, скопировать файл только в область индекса, только в рабочую копию, или сразу в оба места.

      Для закрепления, давайте приведем таблицу сравнения команд git revert, git reset, git checkout и git reset по их взаимодействию с указателем HEAD, историей, индексом и рабочей копией.

      ПРАКТИЧЕСКИЙ БЛОК

      Задание. Библиотека Geometric Lib.

      Сегодня было много теории, пора закрепить ее на практике. Мы снова воспользуемся командой git clone (как и в прошлом уроке). Напомним, что она копирует удаленный репозиторий к вам на компьютер.

      Также напомним, что наш удаленный репозиторий представляет собой библиотеку на Python, которая позволяет высчитывать площади и периметры некоторых геометрических фигур.
      Ссылка на библиотеку Geometric Lib на GitHub: https://github.com/smartiqaorg/geometric_lib

      В этот раз в нашем репозитории появились еще две ветки, теперь его структура выглядит следующим образом:

      Структура библиотеки geometric_lib

      
      # Ветка main (Основная стабильная ветка)
      geometric_lib
          ├── circle.py
          ├── square.py
          └── docs
               └── README.md
      
      # Ветка develop (Ветка разработки)
      geometric_lib
          ├── circle.py
          ├── square.py
          └── docs
               └── README.md
          └── calculate.py
      
      # Ветка feature (Ветка для новых функций)
      geometric_lib
          ├── circle.py
          ├── square.py
          └── docs
               └── README.md
          └── rectangle.py
      

      К файлам добавилось три новых: triangle.py, rectangle.py и calculate.py. Первый содержит функции для вычисления периметра и площади треугольника, второй – то же для квадрата. В свою очередь, calculate.py объединяет функционал этих файлов: он предназначен для расчета площади и периметра переданной пользователем фигуры.

      Задание. Условие.

      1. Клонирование репозитория и знакомство с его структурой

      1.1. Выполните git clone https://github.com/smartiqaorg/geometric_lib. Эта команда создаст директорию geometric_lib/ на вашем компьютере и скопирует наш удаленный репозиторий. Не забудьте перейти в эту директорию командой cd geometric_lib, когда клонирование будет завершено.

      Кстати, когда вы склонируете к себе наш репозиторий, у вас будет только одна локальная ветка: main. Чтобы создать остальные, нужно выполнить git checkout <имя ветки>. Эта команда переключит вас на коммит, на который указывает удаленная ветка и создаст там локальную ветку с таким же именем. Эту команду нужно запустить для каждой ветки отдельно. То есть у вас получится два запуска: для ветки feature и ветки develop.

      1.2. Постройте полный граф истории, чтобы познакомиться со структурой комитов.

      2. Работа с веткой feature

      В последнем коммите ветки feature допущена ошибка. Откатите этот неудачный коммит.

      3. Работа с веткой develop

      Теперь заметьте, что у нас есть два коммита в ветке develop одной и той же тематики: “L-04: Add calculate.py”, “L-04: Update docs for calculate.py”. Объедините их в один коммит и напишите к нему пояснение.

      4. Эксперименты. Работа с файлами calculate.py и rectangle.py в ветке experiments

      Ветку develop мы привели в порядок. Теперь давайте представим, что мы хотим протестировать совместную работу файлов calculate.py и rectangle.py. Чтобы не мешать работе других файлов, создадим отдельную ветку experiment, которая будет брать начало в конце ветки main. Новая ветка будет хранить коммиты с результатами наших экспериментов. Задания:

      4.1. Создайте новую ветку с именем experiment. Как было сказано выше, она пригодится нам, чтобы хранить наши экспериментальные коммиты.

      4.2. Мы хотим провести эксперименты с файлом calculate.py, но текущая документация (файл docs/README.md) устарела. Добавьте в нашу рабочую копию документацию, которая содержит информацию о файле calculate.py. Такая есть, например, в последнем коммите ветки develop. Для этого скопируйте файл docs/README.md из последнего коммита ветки develop в рабочую копию. Подсказка: указатель develop находится на последнем коммите ветки develop.

      4.3. Добавьте в индекс и рабочую копию файл calculate.py из последнего коммита ветки develop.

      4.4. Добавьте все нужные файлы в индекс и сделайте коммит.

      4.5. Мы поняли, что файлы circle.py и square.py могут помешать чистоте наших экспериментов. Удалите их и сделайте коммит.

      Задание. Решение.

      
      # п 1. Клонируем репозиторий и активируем локальные ветки
      $ git clone https://github.com/smartiqaorg/geometric_lib
      $ cd geometric_lib/
      $ git branch --all
      $ git checkout develop
      $ git checkout feature
      $ git branch --all
      
      # п 2. Работаем с веткой feature: откатываем коммит
      $ git checkout feature
      $ git log
      $ git revert HEAD
      $ git log
      
      # п 3. Работа с веткой develop: Объединение коммитов
      $ git checkout develop
      $ git log
      $ git reset --soft HEAD~2
      $ git log
      $ git status
      $ git commit -c ORIG_HEAD
      $ git log
      
      # п 4. Работа с веткой experiment: Перенос и удаление файлов
      $ git checkout main
      $ git checkout -b experiment
      $ git restore --worktree docs/README.md --source develop
      $ git status
      $ git restore --worktree --staged calculate.py --source develop
      $ git status
      $ git add docs/README.md
      $ git commit -m "L-04: Experimental commit"
      $ git rm circle.py square.py 
      $ git status
      $ git commit -m "L-04: Deleted circle and square"
      

      Более подробный разбор задания

      Также на странице Задания и Ответы по курсу Git мы даем более подробный разбор текущего задания.
      Приводим в нем не только команды Git, но и их консольный вывод, а также даем комментарии к каждой команде.

      Как вам материал?

      Читайте также

      Добавить комментарий