2010-03-29 2 views
41

В то время как reset и checkout имеют разные способы использования большую часть времени, я не вижу, какая разница между этими двумя.Есть ли разница между «git reset - hard hash» и «git checkout hash»?

Возможно, есть один или никто бы не беспокоился о добавлении опции --hard, чтобы сделать то, что может сделать базовый checkout.

Возможно, есть разница в том, как вы увидите историю?

+1

Я рассмотрел это в обновлении моего ответа на один из ваших предыдущих вопросов - посмотрите на искусство ascii около вершины, особенно там, где он говорит «Отступление: ...» (столько, сколько я бы хотел больше повторить для повторного ответа на него здесь) – Cascabel

+0

Я действительно думаю, что вы можете опубликовать свой ответ здесь и получить репутацию от него. Если кто-то ищет определенные знания, он не найдет другого поста. Эта цель - очень конкретная тема, и она заслуживает отдельной страницы. Кстати, похоже, вы мой наставник Git :-) harigato, senseï! –

+1

Но я понимаю, разница в том, что сброс перемещает ветку, а не проверку. –

ответ

52

Этот ответ в основном цитируется из моего ответа на предыдущий вопрос: git reset in plain english.

Эти два варианта очень разные. Они приводят к тому же состоянию для вашего индекса и дерева работ, но результирующая история и текущая ветка не совпадают.

Предположим, что ваша история выглядит следующим образом, с мастер-отрасли в настоящее время проверили:

- A - B - C (HEAD, master) 

и запустить git reset --hard B. Вы получите это:

- A - B (HEAD, master)  # - C is still here, but there's no 
          # branch pointing to it anymore 

Вы бы на самом деле получить этот эффект, если вы используете --mixed или --soft тоже - разница лишь в том, что происходит с вашим рабочим деревом и индексом. В случае --hard дерево работы и индекс совпадают B.

Теперь предположим, что вместо этого вы запустите git checkout B. Вы получите это:

- A - B (HEAD) - C (master) 

Вы попали в автономное состояние HEAD. HEAD, дерево работы, индекс все совпадают B, то же, что и с жестким сбросом, но главная ветвь осталась позади на C. Если вы сделаете новый совершить D на данный момент, вы получите это, что, вероятно, не то, что вы хотите:

- A - B - C (master) 
     \ 
     D (HEAD) 

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

+5

+1 как обычно. Этот поток (http://marc.info/?l=git&m=120955970704567&w=2) также добавил один побочный эффект: если вы находитесь в середине слияния (например, при конфликтах слияния или после 'git merge - -no-commit'), 'git reset --hard' забывает о слиянии, но' git checkout -f' не делает; следовательно, «git commit» после того, как последний создаст слияние, что обычно не то, что вы хотите. – VonC

+0

@VonC: и отличная дополнительная точка от вас, как обычно! – Cascabel

14

Если документация, предоставленная с Git, вам не поможет, взгляните на A Visual Git Reference от Марка Лодато.

В частности, если вы сравниваете git checkout <non-branch> с git reset --hard <non-branch> (hotlinked):

git checkout master~3 http://marklodato.github.com/visual-git-guide/checkout-detached.svg.png

git reset --hard master~3 http://marklodato.github.com/visual-git-guide/reset-commit.svg.png

Обратите внимание, что в случае git reset --hard master~3 вы оставляете часть DAG пересмотров - некоторые из на фиксацию не ссылается ни одна ветка. Они защищены (по умолчанию) 30 дней по reflog; они в конечном итоге будут обрезаны (удалены).

6

git-reset hash устанавливает привязку ответвления к данному хэшу и, при необходимости, проверяет его с помощью --hard.

git-checkout hash устанавливает рабочее дерево в заданный хэш; и если хеш не является именем ветки, вы получите отдельную голову.

в конечном счете, Git имеет дело с 3-мя вещами:

    working tree (your code) 
------------------------------------------------------------------------- 
        index/staging-area 
------------------------------------------------------------------------- 
     repository (bunch of commits, trees, branch names, etc) 

git-checkout по умолчанию просто обновляет индекс и рабочее дерево, и может при необходимости обновлять что-то в хранилище (с опцией -b)

git-reset по умолчанию обновляет репозиторий и индекс, а также, возможно, рабочее дерево (с опцией --hard)

Вы можете думать о репозитории как это:

HEAD -> master 

refs: 
    master -> sha_of_commit_X 
    dev -> sha_of_commit_Y 

objects: (addressed by sha1) 

    sha_of_commit_X, sha_of_commit_Y, sha_of_commit_Z, sha_of_commit_A .... 

git-reset манипулирует, что ссылки ветви указывают.

Предположим, что ваша история выглядит следующим образом:

  T--S--R--Q [master][dev] 
     /
    A--B--C--D--E--F--G [topic1] 
        \ 
        Z--Y--X--W [topic2][topic3] 

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

Таким образом, вы имеете следующие филиалы:

master -> Q 
dev -> Q 
topic1 -> G 
topic2 -> W 
topic3 -> W 

И текущая ветвь topic2, то есть точки с головы до topic2.

HEAD -> topic2 

Затем git reset X сбросит имя topic2, чтобы указать на X; что означает, что если вы сделаете фиксацию P в разделе topic2, все будет выглядеть так:

  T--S--R--Q [master][dev] 
     /
    A--B--C--D--E--F--G [topic1] 
        \ 
        Z--Y--X--W [topic3] 
          \ 
          P [topic2] 
Смежные вопросы