Что именно делает git fetch?
Edit: я проверил это что означает FETCH_HEAD в Git? прежде чем задавать вопрос.
Извините за оригинальный неточный вопрос.
мой вопрос в том, как fetch действительно работает? Fetch отбрасывает весь текущий журнал?
Это моя ситуация: мои товарищи по команде и я используем один и тот же репозиторий, который имеет только одну ветку. Так что мы должны сделать, принести, прежде чем толкать что-нибудь.
Обычно мы делаем это так:
git status
git add .
git commit -m message1
git fetch origin
git reset head
git status
git add .
git commit -m message
git push
но после сброса кажется, что моя предыдущая фиксация (с message1
) ушел.
это нормально или что-то не так?
Как я могу получить доступ к моей местной истории?
Они синхронизированы, но моя местная история исчезла.
старый персонал, забудьте об этом: я недавно изучал git CLI.
Кто-то велел мне напечатать:"git fetch head
" для отслеживания удаленных филиалов.
Но интересно, что это делает ? Эта команда переопределяет мой локальный журнал?
И в чем разница между "git fetch
" и "git fetch head
" ?
2 ответов
git fetch
сам по себе довольно прост. Сложных деталей до и после.
первое, что нужно знать, это то, что Git stores совершает. Фактически, это, по сути, то, что Git: он управляет коллекцией коммитов. Эта коллекция редко сжимается: по большей части, единственное, что вы когда-либо делать с этой коллекцией совершает добавить новые коммиты.
Commits, индекс и дерево труда!--144-->
каждая фиксация содержит несколько фрагментов информации, таких как имя автора и адрес электронной почты, а также отметку времени. Каждая фиксация также сохраняет полный снимок всех файлов, которые вы сказали: это файлы, хранящиеся в вашем индекс (также известный как постановка площадью) в то время вы бежали git commit
. Это также относится к коммитам, которые вы получаете от кого-то другого: они сохраняют файлы, которые были в индексе другого пользователя в то время, когда другой пользователь ran git commit
.
обратите внимание, что каждый репозиторий имеет только один индекс, по крайней мере вначале. Этот индекс связан с one работа-дерево. В новых версиях Git, вы можете использовать git worktree add
для добавления дополнительных рабочих деревьев; каждое новое рабочее дерево поставляется с одним новым индексом/промежуточной областью. Точка этого индекса должна выступать в качестве промежуточного держателя файла, расположенного между " текущей фиксацией "(aka HEAD
) и дерево работы. Изначально HEAD
commit и индекс обычно совпадает: они содержат одинаковые версии всех зафиксированных файлов. Git копирует файлы из HEAD
в индекс, а затем из индекса в дерево работы.
легко увидеть дерево работы: в нем есть ваши файлы в обычном формате, где вы можете просматривать и редактировать их со всеми обычными инструментами на вашем компьютере. Если вы пишете код Java или Python или HTML для веб-сервера, файлы рабочего дерева могут использоваться компилятором, интерпретатором или веб-сервером. Файлы, хранящиеся в индексе и хранящиеся в каждом git commit, do не имеют эту форму и являются не используется компиляторами, интерпретаторами, веб-серверами и т. д.
еще одна вещь, которую нужно помнить о фиксациях, - это то, что как только файл находится в фиксации, он невозможно изменить. Никакая часть любого обязательства не может измениться. Таким образом, фиксация является постоянной-или, по крайней мере, постоянной, если она не удалена (что может быть сделано, но трудно и обычно нежелательно). Однако то, что находится в индексе и дереве работы, может быть изменено в любое время. Вот почему они существуют: индекс почти является "изменяемой фиксацией" (за исключением того, что он не сохраняется, пока вы не запустите git commit
), а дерево работы сохраняет файлы в том виде, в котором может использовать остальная часть компьютера.1
1не надо и индекс и работа-дерево. VCS может лечить дерево работы как "изменяемая фиксация". Вот что делает Mercurial; вот почему Mercurial не нуждается в индексе. Возможно, это лучший дизайн,но это не так, как работает Git, поэтому при использовании Git у вас есть индекс. Наличие индекса-это большая часть того, что делает Git таким быстрым: без него Mercurial должен быть очень умным и все еще не таким быстрым, как Git.
коммиты помнят своего родителя; новые коммиты-это дети
когда вы делаете новая commit, запустив git commit
, Git берет содержимое индекса и делает постоянный снимок всего, что находится в нем прямо в этот момент. (Вот почему вы должны git add
файлы: вы копируете их из своего рабочего дерева, где вы их изменили, обратно в свой индекс, чтобы они были готовы "сфотографироваться"для нового снимка.) Git также собирает сообщение фиксации и, конечно же, использует ваше имя и адрес электронной почты и текущее время, чтобы сделать новый совершать.
но Git также сохраняет в новом коммите хэш ID текущего коммита. Мы говорим, что новая фиксация "указывает назад" на текущую фиксацию. Рассмотрим, например, этот простой репозиторий с тремя фиксациями:
A <-B <-C <-- master (HEAD)
здесь мы говорим, что филиала master
"указывает на" третий коммит, который я обозначил C
, а не использовать один из непонятных идентификаторов хэша Git, таких как b06d364...
. (Имя HEAD
относится к имени филиала, master
. Вот как Git может повернуть строку HEAD
в правильный хэш ID: Git следует HEAD
to master
, затем считывает идентификатор хэша из master
.) Это commit C
сам, что "указывает на" - сохраняет хэш-идентификатор-commit B
, однако; и совершить B
точки для фиксации A
. (С момента совершения A
является самым первым фиксацией когда-либо, нет более раннего фиксации для него, чтобы указать, поэтому он вообще никуда не указывает, что делает его немного особенный. Это называется root commit.)
сделать новая commit, git упаковывает индекс в снимок, сохраняет его с вашим именем и адресом электронной почты и так далее, и включает хэш-идентификатор commit C
, чтобы сделать новую фиксацию с новым хэш-идентификатором. Мы будем использовать D
вместо нового хэш-идентификатора, поскольку мы не знаем, каким будет новый хэш-идентификатор:
A <-B <-C <-D
обратите внимание, как D
указывает на C
. Теперь что сюда изменяет идентификатор хэша, хранящийся под именем master
, в магазине D
хэш ID вместо C
. Имя, сохраненное в HEAD
сам по себе совсем не меняется: он все еще master
. Итак, теперь у нас есть это:
A <-B <-C <-D <-- master (HEAD)
вы можете видеть из этой диаграммы, как работает Git: дано имя, как master
, Git просто следует стрелке, чтобы найти последний commit. Эта фиксация имеет стрелку назад к ее раньше или родитель commit, который имеет еще одну стрелку назад к своему родителю, и так далее, во всех своих предках, ведущих обратно к корневому commit.
обратите внимание, что в то время как дети помнят своих родителей, родительские коммиты не помнят своих детей. Это потому что не совершал и никогда не изменит: Git буквально не могу добавьте детей к родителю, и он даже не пытается. Git должен всегда работать назад, от нового к старому. Стрелки фиксации автоматически указывают назад, поэтому обычно я даже не рисую их:
A--B--C--D <-- master (HEAD)
распределенные репозитории: какие git fetch
тут
при использовании git fetch
, мы два разных Gits, с другой-а по теме-хранилищ. Предположим, у нас есть два репозитория Git на двух разных компьютерах, которые начинаются с тех же трех commits:
A--B--C
поскольку они начинаются с одинаковых коммитов, эти три коммита также имеют одинаковые идентификаторы хэша. Эта часть очень умна и является причиной того, что хэш-идентификаторы таковы: хэш-идентификатор-контрольная сумма2 на содержание фиксации, так что любые два коммита, которые точно идентичны, всегда имеют то же самое хэш-код.
теперь вы, в вашем Git и вашем репозиторий, добавили новый commit D
. Тем временем они-кто бы они ни были-возможно, добавили свои собственные новые обязательства. Мы будем использовать разные буквы, так как их коммиты обязательно будут иметь разные хэши. Мы также рассмотрим это в основном с вашей (Гарри) точки зрения; мы будем называть их "Салли". Мы добавим еще одну вещь к нашей картине код репозиторий: теперь он выглядит так:
A--B--C <-- sally/master
\
D <-- master (HEAD)
теперь предположим, что Салли сделала два совершает. В ее хранилище она это:
A--B--C--E--F <-- master (HEAD)
или, может быть (если она принесет от вас, но еще не побежала git fetch
):
A--B--C <-- harry/master
\
E--F <-- master (HEAD)
, когда вы выполнить git fetch
, вы подключаете свой Git к Git Салли и спрашиваете ее, есть ли у нее новые коммиты, добавленные в ее master
С момента совершения C
. Она делает-у нее есть ее новые коммиты E
и F
. Так что ваш Git получает эти коммиты от нее, вместе со всем необходимым для завершения снимков для этих коммитов. Затем ваш Git добавляет эти коммиты в код репозитория, так что теперь у вас есть это:
E--F <-- sally/master
/
A--B--C
\
D <-- master (HEAD)
Как видите, что git fetch
неужели для вас было собрать все ее новая совершает и добавить их в свой репозиторий.
для того, чтобы вспомнить, где ее master
теперь, когда вы поговорили с ней Git, ваш Git копирует ее master to код sally/master
. Твой собственный!--24--> и свой HEAD
, не менять вовсе. Только эти имена "памяти другого репозитория Git", которые git называет удаленное отслеживание названия ветвей, изменить.
2этот хэш является криптографическим хэшем, отчасти поэтому трудно обмануть Git, и отчасти потому, что криптографические хэши естественно ведут себя хорошо для целей Git. Текущий хэш использует SHA-1, который был безопасный, но видел атаки грубой силы и теперь оставлен для криптографии. Git, скорее всего, перейдет на SHA2-256 или SHA3-256 или какой-либо другой более крупный хэш. Будет переходный период с некоторыми неприятностями. :-)
теперь вы должны объединить или перебазировать -git reset
это вообще не так
обратите внимание, что после того, как вы получили от Салли, это код репозиторий, и только ваш репозиторий, который имеет всю работу от вас обоих. У Салли все еще нет вашего нового commit D
.
это все еще верно, даже если вместо "Салли" ваш другой Git называется origin
. Теперь, когда у вас есть оба!--24--> и origin/master
, вы должны сделать что-то, чтобы подключить новый commit D
С их последнего коммита F
:
A--B--C--D <-- master (HEAD)
\
E--F <-- origin/master
(я переехала D
сверху по причинам рисования графика, но это тот же график, что и прежде чем,
ваши основные два варианта здесь, чтобы использовать git merge
или git rebase
. (Есть и другие способы сделать это, но это два, чтобы узнать.)
слияние на самом деле проще как git rebase
делает что-то, что включает в себя глагольную форму слияния,слияние. Что?!--71--> делает это, чтобы запустить форму глагола слияния, а затем зафиксировать результат как новая commit, который называется объединить commit или просто "сольют", которых является существительное, образованное от слияния. Мы можем нарисовать новый commit слияния G
таким образом:
A--B--C--D---G <-- master (HEAD)
\ /
E--F <-- origin/master
в отличие от обычного коммита, a объединить commit и два родителей.3 он подключается к обоим из двух предыдущих коммитов, которые были использованы для слияния. Это позволяет подтолкнуть ваш новый commit G
to origin
: G
берет с собой ваш D
, но также подключается обратно к их F
, так что их Git Хорошо с этим новым обновлением.
это слияние - такое же слияние, которое вы получаете от слияния двух ветвей. И в самом деле, ты!--136-->сделал объединить две ветви здесь: вы объединили свой master
С Салли (или origin
' s)master
.
используя git rebase
обычно легко, но это сложнее. Вместо слияние ваш commit D
С их фиксацией F
сделать новый объединить commit G
, что git rebase
это скопировать каждый из ваших коммитов, так что новый копии, которые являются новыми и разными фиксациями, приходят после последней фиксации на вашем вверх.
здесь ваш апстрим origin/master
, и совершает, что у вас есть, что они не только совершают D
. Так что git rebase
делает скопировать of D
, который я называю D'
, размещение копии после их фиксации F
, так что D'
'родитель ы F
. Промежуточный график выглядит так:5
A--B--C--D <-- master
\
E--F <-- origin/master
\
D' <-- HEAD
процесс копирования использует тот же код слияния, что и git merge
использует для выполнения глагольной формы,слияние, ваших изменений от commit D
.4 как только копия сделана, однако, код rebase видит, что больше нет коммитов для копирования, поэтому он тогда изменения код master
ветке точки в конечную скопировал совершить D'
:
A--B--C--D [abandoned]
\
E--F <-- origin/master
\
D' <-- master (HEAD)
это отказывается от первоначальной фиксации D
.6 это означает, что мы можем прекратить рисовать его тоже, так что теперь мы получаем:
A--B--C--E--F <-- origin/master
\
D' <-- master (HEAD)
теперь это легко git push
ваш новый commit D'
на origin
.
3в Git (но не Mercurial) фиксация слияния может иметь более двух родителей. Это не делает ничего, что вы не можете сделать путем повторного слияния, поэтому это в основном для хвастовства. :-)
4технически, фиксация базы слияния, по крайней мере для этого случая, является фиксацией C
и два наконечника фиксации D
и F
, так что в этом случае это буквально то же самое. Если вы перебазировать больше, чем один коммит, это становится немного сложнее, но в принципе это все равно просто.
5это промежуточное состояние, где HEAD
отсоединяется от master
, обычно невидимы. Ты увидишь это, только если что-то идет не так во время глагольной формы слияния, так что Git останавливается и должен получить от вас помощь, чтобы закончить операцию слияния. Когда тут возникают, когда есть конфликт слияния во время перебазирования-важно знать, что Git-это в "отрезанная голова" государства, но пока перебазировать выполняет самостоятельно, вам не придется заботиться об этом так много.
6исходная цепочка фиксации временно сохраняется через Git reflogs и через имя ORIG_HEAD
. The ORIG_HEAD
значение перезаписывается следующей операцией, которая вносит "большие изменения", и запись reflog в конечном итоге истекает, как правило, через 30 дней для этой записи. После этого git gc
действительно удалит исходную цепочку фиксации.
на
вам не нужно делать два отдельных коммита, и git fetch
не будет ронять журнал.
--o--o--o (origin/master)
\
x--x (master: my local commits)
что вы должны сделать, это перебазировать локальную фиксацию поверх любой новой фиксации, полученной :
git fetch
--o--o--o--O--O (origin/master updated)
\
x--x (master)
git rebase origin/master
--o--o--o--O--O (origin/master updated)
\
x'--x' (master rebased)
git push
--o--o--o--O--O--x'--x' (origin/master, master)
даже проще, с Git 2.6, я бы использовал config:
git config pull.rebase true
git config rebase.autoStash true
и git pull
будет автоматически воспроизводить ваши локальные коммиты поверх origin/master
. Тогда вы можете git push
.