Как обновить клон git shallow?

фон

(для tl;dr, см. #вопросы ниже)

у меня есть несколько мелких клонов репозитория git. Я использую мелкие клоны, потому что они намного меньше по сравнению с грязным клоном. Каждый клонированный делает о git clone --single-branch --depth 1 <git-repo-url> <dir-name>.

это работает нормально, за исключением того, что я не вижу, как его обновить.

когда я клонирую тег, обновление не имеет смысла, так как тег заморожен во времени (как я понимаю). В этом случае, если я хочу обновить, это означает, что я хочу клонировать другой тег, поэтому я просто rm -rf <dir-name> и снова клон.

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

пробовал git pull --depth 1 но хотя я не должен ничего толкать в удаленный репозиторий, он жалуется, что не знает, кто я.

пробовал git fetch --depth 1, но хотя он, кажется, что-то обновляет, я проверил, что он не обновлен (некоторые файлы в удаленном репозитории имеют другой содержание, чем на моем клоне).

после https://stackoverflow.com/a/20508591/279335, я пытался git fetch --depth 1; git reset --hard origin/master, но две вещи: во-первых, я не понимаю, почему git reset необходимо, во-вторых, хотя файлы, кажется, в актуальном состоянии, некоторые старые файлы остаются, и git clean -df не удаляет эти файлы.

вопросы

пусть клон создан с git clone --single-branch --depth 1 <git-repo-url> <dir-name>. Как обновить его, чтобы достичь того же результата, что и rm -rf <dir-name>; git clone --single-branch --depth 1 <git-repo-url> <dir-name>? Или rm -rf <dir-name> и клон опять единственный выход?

Примечание

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

2 ответов


[слегка переработано и отформатировано] учитывая клон, созданный с помощью git clone --single-branch --depth 1 url directory, как я могу обновить его, чтобы достичь того же результата, что и rm -rf directory; git clone --single-branch --depth 1 url directory?

обратите внимание, что --single-branch это по умолчанию при использовании --depth 1. (Единственная) ветвь-это та, которую вы даете с -b. Существует длинная сторона, которая идет здесь об использовании -b теги, но я оставлю это на потом. Если ты ... --206-->не использовать -b, ваш Git спрашивает "вверх" Гит-Гит в URL-адресом-филиала это проверил и делает вид, что вы использовали -b thatbranch. Это означает, что важно быть осторожным при использовании --single-branch без -b чтобы убедиться, что текущая ветвь этого вышестоящего репозитория разумна, и, конечно же, когда вы do использовать -b, чтобы убедиться, что аргумент ветви, который вы даете, действительно называет ветку, а не тег.

простой ответ в основном этот, с двумя небольшими изменениями:

после https://stackoverflow.com/a/20508591/279335, я пытался git fetch --depth 1; git reset --hard origin/master, но две вещи: во-первых, я не понимаю, почему git reset необходимо, во-вторых, хотя файлы, кажется, в актуальном состоянии, некоторые старые файлы остаются, и git clean -df не удаляет эти файлы.

два небольших изменения: убедитесь, что вы используете origin/branchname вместо этого, и добавить -x (git clean -d -f -x или git clean -dfx) к git clean шаг. Что касается почему, это становится немного сложнее.

что происходит

без --depth 1 на git fetch step вызывает другой Git и получает от него список имен ветвей и соответствующих хэш-идентификаторов фиксации. То есть, он находит список все ветви восходящего потока и их текущие коммиты. Тогда, потому что у вас есть --single-branch хранилище код Git выбрасывает все, кроме одной ветви, и переносит все, что нужно Git для подключения этой текущей фиксации обратно к фиксации(фиксациям), которые у вас уже есть в вашем репозитории.

с --depth 1, ваш Git не беспокоится о подключении нового фиксации к старым историческим фиксациям вообще. Вместо этого он получает только одну фиксацию и другие объекты Git, необходимые для завершения этой фиксации. Затем он пишет дополнительную запись "мелкий трансплантат", чтобы отметить, что одна фиксация как новая псевдо-корневая фиксация.

обычный (неглубокий) клон и fetch

все это связано с тем, как Git ведет себя, когда вы используете нормальный (неглубокий, не одно ветвь) клон:git fetch вызывает восходящий Git, получает список всего, а затем приносит все, что у вас еще нет. Вот почему первоначальный клон настолько медленный, а fetch-to-update обычно так быстро: как только вы получаете полный клон, обновления редко имеют очень много принесите: может быть, несколько коммитов, может быть, несколько сотен, и большинству из этих коммитов тоже не нужно много.

история репозитория формируется из коммитов. Каждый совершает имена свои родитель commit (или для слияний, родительских коммитов, множественного числа), в цепочке, которая идет назад от "последнего коммита", к предыдущему коммиту, к некоторому более-родовому коммиту и так далее. Цепочка в конечном итоге останавливается, когда она достигает фиксации, у которой нет родителя, например первого фиксация всегда производится в репозитории. Этот вид фиксации является root commit.

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

o <- o <- o <- o   <-- master

имя master указывает на четвертый и последний коммит, который указывает на третий, который указывает на второй, который указывает на первый.

каждая фиксация несет с собой полный снимок всех файлов, которые входят в эту фиксацию. Файлы, которые не изменились shared через эти коммиты: четвертый коммит просто "заимствует" неизмененную версию из третьего коммита, который "заимствует" ее из второго и так далее. Следовательно, каждый фиксатор называет все" объекты Git", которые ему нужны, и Git либо находит эти объекты локально-потому что они уже есть-или использует fetch протокол, чтобы привести их из другого, вверх по течению Git. Есть формат сжатия называется "упаковка", а специальный вариант для сетевой передачи называется" тонкие пакеты", что позволяет Git делать это еще лучше / увлекательнее, но принцип прост: Git нужны все, и только те объекты, которые идут с новыми коммитами, которые он собирает. Ваш Git решает, имеет ли он эти объекты, а если нет, получает их от своего Git.

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

        o--o   <-- feature/tall
       /
o--o--o---o    <-- master
    \    /
     o--o      <-- bug/short

здесь bug/short сливается обратно в master, в то время как филиал feature/tall все еще находится в стадии разработки. The имя bug/short может (вероятно) теперь быть полностью удален: нам это больше не нужно, если мы закончили делать коммиты на нем. Фиксация на кончике master имена два предыдущие коммиты, включая фиксацию на кончике bug/short, так что по fetching master мы принесем the bug/short commits.

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

 o--o
     \
o--o--o   <-- master

или это один:

 o--o     <-- orphan

o--o      <-- master

на самом деле, тот, у которого только один master вероятно, было сделано путем слияния orphan на master, затем удаление имени orphan.

трансплантаты и замены

Git долгое время имел (возможно, шаткую) поддержку лоскутов, который был заменен (намного лучше, на самом деле-solid) поддержкой generic замены. Чтобы понять их конкретно нам нужно добавить, к вышесказанному, понятие что каждая фиксация имеет свой собственный уникальный ID. Эти идентификаторы-большие уродливые 40-символьные хеши SHA-1,face0ff... и так далее. На самом деле, объект Git имеет уникальный идентификатор, хотя для целей графика все, что нас волнует, - это коммиты.

для рисования графиков эти большие хэш-идентификаторы слишком болезненны для использования, поэтому мы можем использовать однобуквенные имена A через Z вместо. Давайте снова используем этот график, но добавим однобуквенные имена:

        E--H   <-- feature/tall
       /
A--B--D---G    <-- master
    \    /
     C--F      <-- bug/short

Commit H ссылается на совершение E (E is H ' s родитель). Commit G, который является объединить commit - это означает, что у него есть по крайней мере два родителя-относится к обоим D и F и так далее.

обратите внимание, что ветка имена, feature/tall, master и bug/short каждый пункт один коммит. Имя bug/short указывает на совершение F. Вот почему commit F на ветке bug/short ... но так это commit C. Commit C на bug/short потому что это достижим от имени. Имя возвращает нас к F и F нам C, так что C на филиал bug/short.

обратите внимание, однако, что commit G, кончик master, заставляет нас совершить F. Это означает, что совершение F is и в филиал master. это ключевая концепция в Git: коммиты могут быть на один, много, или даже нет филиалы. имя ветви-это просто способ начать работу в графе фиксации. Есть и другие способы, такие как имена тегов refs/stash (что приводит вас к текущему тайнику: каждый тайник на самом деле является парой коммитов) и reflogs (которые обычно скрыты от просмотра, поскольку они обычно просто беспорядок).

это, однако, приводит нас к имплантации и замены. Трансплантат-это просто ограниченный вид замены, и мелкий репозитории используют ограниченную форму трансплантата.1 я не буду описывать замены полностью здесь, поскольку они немного сложнее, но в целом, что Git делает для всех из них, это использовать трансплантат или замену как "вместо". Для конкретного случая commits, то, что мы хотим здесь, чтобы иметь возможность изменить-или, по крайней мере,претендует to change-Родительский идентификатор или идентификаторы любой фиксации ... и мелкий репозитории, мы хотим иметь возможность притворяться, что данная фиксация имеет нет родителей.


1способ использования мелкими репозиториями кода трансплантата -не хлипкая. Для более общего случая, я рекомендовал использовать git replace вместо этого, как это и было и есть не хлипкая. Единственное рекомендуемое применение для трансплантатов-или, по крайней мере, было много лет назад-поставить их на место достаточно долго для запуска git filter-branch to скопировать измененная привитая история, после чего вы должны просто полностью отказаться от привитой истории. Вы можете использовать git replace для этой цели, но в отличие от трансплантатов, вы можете использовать git replace постоянно или полупостоянно, без необходимости git filter-branch.


создание мелкого клона

чтобы сделать неглубокий клон глубины 1 текущего состояния вышестоящего репозитория, мы выберем один из трех названия ветвей -feature/tall, master или bug/short-и перевести его на идентификатор коммита. Затем мы напишем специальную графтовую запись, в которой говорится: "когда вы увидите эту фиксацию, притворитесь, что она имеет нет родительские коммиты, т. е. является корневой фиксацией."

предположим, мы выбираем master. Имя master точки для фиксации G, так что мелкий клон совершения G, получаем commit G из верхнего Git, как обычно, но затем напишите специальный прививка запись, которая утверждает commit G и нет родителей. Мы поместили это в наш репозиторий, и теперь наш график выглядит так:

G   <-- master, origin/master

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

обновление мелкого клона, который мы сделали раньше

но что, если у нас уже есть (глубина-1 мелкий) клон, и мы хотим обновление это? Ну, это не проблема. Предположим, мы сделали мелкий клон вверх по течению, когда master указал на commit B, перед новыми ветвями и исправлением ошибки. Это означает, что мы в настоящее время есть это:

B   <-- master, origin/master

пока Bнастоящий родитель A, у нас есть запись трансплантата мелкого клона, говорящая " притворяться B корень совершить". Теперь мы!--103-->, который ищет апстрима master-вещь мы вызов origin/master - и видит commit G. Мы берем совершения G сверху, вместе со своими объектами, но намеренно не захватить совершает D и F. Затем мы обновляем наши записи трансплантата мелкого клона, чтобы сказать " притворяться G является корневой фиксацией тоже":

B   <-- master

G   <-- origin/master
наш репозиторий два корень совершившее имя master (все еще) указывает на commit B, чьи родители мы (до сих пор) притворяемся несуществующими, и имя origin/master указывает на G, чьи родители мы притворяемся несуществующими.

вот почему вам нужно git reset

в обычном репозитории вы можете использовать git pull, что действительно git fetch следовал по git merge. Но!--118--> требует истории, и у нас ее нет: мы подделали Git с притворными корневыми коммитами, и у них нет история позади них. Поэтому мы должны использовать .

что git reset это немного сложно, потому что это может повлиять на три разные вещи: a филиала, the индекс и работа-дерево. Мы уже видели, что такое имена ветвей: они просто указывают на (один, конкретный) коммит, который мы называем совет филиала. Остается индекс и дерево работ.

в работа-дерево легко объяснить: это где находятся все ваши файлы. Вот так: ни больше, ни меньше. Это там, так что вы можете на самом деле использовать Git: Git-это сохранение всех когда-либо сделанных фиксаций навсегда, чтобы их можно было извлечь. Но они в формате бесполезны для простых смертных. Быть используется, файл-или, как правило, целая сумма файлов фиксации-должна быть извлечена в нормальный формат. Дерево работы-это то, где это происходит, и тогда вы можете работать над ним и делать новые коммиты, используя его.

на индекс немного сложнее объяснить. Это что-то особенное для Git: другие системы управления версиями не имеют его, или если у них есть что-то подобное, они не раскрывают его. ГИТ знает. Индекс Git по существу, где вы держите далее совершить, но это означает, что он начинает держать настоящее зафиксировать, что вы извлекли в work-tree, и Git использует это, чтобы сделать Git быстрым. Мы еще поговорим об этом чуть позже.

что git reset --hard не влияет все три: имя ветви, индекс и дерево работы. Это движется имя ветви, так что оно указывает на (возможно, другое) фиксацию. Затем он обновляет индекс в соответствии с этой фиксацией и обновляет дерево работы в соответствии с новым индексом.

отсюда:

git reset --hard origin/master

предписывает искать origin/master. С тех пор, как мы запустили наш git fetch, что теперь указывает на commit G. Git тогда делает наши master - наша текущая (и только) ветка - также указывает на commit G, а затем обновляет наш индекс и работы-дерево. Наш график теперь выглядит так:

B   [abandoned - but see below]

G   <-- master, origin/master

теперь master и origin/master оба имени commit G, и совершить G - это тот, который зарегистрирован в дереве работы.

зачем нужен git clean -dfx

ответ здесь немного сложно, но обычно это "вы не делаете "(нужно git clean).

когда вы do нужно git clean, это потому, что вы-или то, что вы запустили-добавили файлы в свое рабочее дерево, о котором вы не сказали Git. Это не отслежен и/или игнорировать файлы. Используя git clean -df удалить не отслежен файлы (и пустые каталоги); добавление -x также удалит игнорируемые файлы.

подробнее о разница между "untracked" и "ignored", см. ответ.

почему вам не нужен git clean индекс

я упоминал выше, что вам обычно не нужно запускать git clean. Это из-за индекса. Как я уже говорил ранее, индекс Git-это в основном "следующая фиксация". Если вы никогда не добавляете свои собственные файлы-если вы просто используете git checkout чтобы проверить различные существующие коммиты, которые у вас были все время, или которые вы добавили с git fetch; или если вы используете git reset --hard чтобы переместить имя ветви, а также переключить индекс и дерево работы на другую фиксацию-тогда все, что находится в индексе прямо сейчас здесь , потому что ранее git checkout (или git reset) поставить это в индексе, а также в дереве работы.

другими словами, индекс имеет короткий-и быстрый для Git доступ -резюме или манифест с описанием текущей дерево работы. Git использует это, чтобы узнать, что сейчас находится в дереве работы. Когда вы просите Git переключиться на другую фиксацию, через git checkout или git reset --hard, Git может быстро сравнить существующий индекс с новой фиксацией. Любые файлы изменить, Git должен извлечь из новой фиксации (и обновить индекс). Любые файлы, которые недавно добавил, Git также должен извлечь (и обновить индекс). Любые файлы, которые ушел - которые находятся в существующем индексе, но не в новом commit-Git должен удалить ... и это то, что делает Git. Git обновляет, добавляет и удаляет эти файлы в рабочем дереве, как указано сравнением между текущим индексом и новой фиксацией.

это означает, что если вы do нужно git clean, вы должны были сделать что-то вне Git, который добавил файлы. Эти добавленные файлы не находятся в индексе, поэтому по определению, они не отслеживаются и / или игнорируются. Если они просто не отслеживаются,git clean -f удалил бы их, но если они игнорируются, только git clean -fx удалит их. (Вы хотите -d просто удалить каталоги, которые являются или становятся пустыми во время чистки.)

заброшенные коммиты и сбор мусора

я упомянул, и нарисовал в обновленном мелком графике, что когда мы git fetch --depth 1 а то git reset --hard, мы отказ предыдущая фиксация мелкого графика глубины-1. (На графике, который я нарисовал, это было совершить B.) Однако в Git заброшенные коммиты редко действительно заброшены-по крайней мере, не сразу. Вместо этого некоторые специальные имена, такие как ORIG_HEAD держитесь за них некоторое время, и каждый ссылка-ветви и теги являются формами ссылки-несет с собой log "предыдущие значения".

вы можете отобразить каждый reflog с git reflog refname. Например, git reflog master показывает вам не только какие commit master имена теперь, но и какие коммиты он назвал в прошлом. Существует также reflog для HEAD сам, что и есть git reflog показывает по умолчанию.

записи Reflog в конечном итоге истекает. Их точная продолжительность варьируется, но по умолчанию они имеют право на истечение после 30 дней в некоторых случаях и 90 дней в других. Как только они истекают, эти записи reflog больше не защищают заброшенные коммиты (или, для аннотированных ссылок на теги, аннотированные tag object-теги не должно для перемещения, так что этот случай не должно произойти, но если это произойдет-если вы заставите Git переместить тег-он просто обрабатывается так же, как и все другие ссылки).

как только любой объект git-commit, аннотированный тег, "дерево" или "blob" (файл)-это действительно unreferenced, Git разрешено удалить его по-настоящему.2 только на этом этапе базовые данные репозитория для коммитов и файлы исчезают. Даже тогда, это происходит только тогда, когда что-то работает git gc. Таким образом, мелкий репозиторий обновляется с помощью git fetch --depth 1 не совсем то же самое, что и свежий клон с --depth 1: мелкий репозиторий, вероятно, имеет некоторые затяжные имена для исходных коммитов и не будет удалять дополнительные объекты репозитория, пока эти имена не истечут или не будут иным образом очищены.


2кроме проверки ссылки, объекты получают минимум времени прежде чем они истекают. Значение по умолчанию-две недели. Это предотвращает git gc от удаления временных объектов, которые git создает, но еще не установил ссылку. Например, при создании новой фиксации Git сначала превращает индекс в ряд tree объекты, которые ссылаются друг на друга, но не имеют верхнего уровня ссылка. Затем он создает новый commit объект, который ссылается на дерево верхнего уровня, но еще ничего не относится к фиксации. Наконец, он обновляет текущее название филиала. Пока этот последний шаг не завершится, деревья и новая фиксация недоступны!


особенности --single-branch и/или мелкие клоны

я отметил выше, что имя git clone -b может относиться к tag. Для нормальных (неглубоких или не-одинарных) клонов это работает так же, как и следовало ожидать: вы получаете обычный клон, а затем Git делает git checkout по тегу name. В результате получается обычный отсоединенный Голова, в совершенно обычном клоне.

с мелкими или одноветвевыми клонами, однако, есть несколько необычных последствий. Все это, в некоторой степени, результат того, что Git позволяет реализации показать себя.

во-первых,если вы используете --single-branch, Git изменяет нормальный fetch конфигурация в новом репозитории. Нормальный fetch конфигурация зависит от имени, которое вы выбрали для пульт ДУ, но по умолчанию origin поэтому я просто буду использовать origin здесь. Она гласит:"!--203-->

fetch = +refs/heads/*:refs/remotes/origin/*

опять же, это нормальная конфигурация для нормальный (не одно-ветвь) клон. Эта конфигурация сообщает git fetch что надо принести, что "все ветви". Когда вы используете --single-branch, однако, вы получаете вместо этого строку выборки, которая относится только к одной ветви:

fetch = +refs/heads/zorg:refs/remotes/origin/zorg

если вы клонируете zorg филиала.

в зависимости от того ветвь, которую вы клонируете, это та, которая входит в fetch линии. каждого будущее git fetch будет подчиняться этой строке3 таким образом, вы не получите никаких других ветвей. Если ты ... --206-- > do хотите получить другие ветви позже, вам придется изменить эту строку или добавить больше строк.

второе, если вы используете --single-branch и что ты клон-это тег, Git поставит довольно странный fetch линии. Например, с git clone --single-branch -b v2.1 ... I получить:

fetch = +refs/tags/v2.1:refs/tags/v2.1

это означает, что вы получите нет ветви, и если кто-то не переместил тег,4git fetch сделает ничего!

в-третьих, поведение тега по умолчанию немного странно из-за способа git clone и git fetch получить теги. Помните, что теги-это просто ссылка на одну конкретную фиксацию, так же как ветви и все другие ссылки. Есть два ключевых различия между ветви и теги, хотя: ветви ожидается переместить (и тегов), и ветки переименован (а теги-нет).

помните, что все вышесказанное, мы продолжаем находить, что другой (вверх по течению) Git's master становится origin/master и так далее. Это пример процесса переименования. Мы также видели, кратко, точно как что переименование работает, через fetch = линия: наш Git берет их refs/heads/master и меняет его на наш refs/remotes/origin/master. Это название не только отличается-просмотр (origin/master), но буквально не могу быть таким же, как любой из наших филиалов. Если мы создадим ветку с именем origin/master,5 "полное имя" этой ветви на самом деле refs/heads/origin/master который отличается от другого полного имени refs/remotes/origin/master. Только когда Git использует более короткое имя, у нас есть одна (обычная, локальная) ветвь с именем origin/master и другой (удаленное отслеживание) ветка с именем origin/master. (Это очень похоже на пребывание в группа, где всех зовут Брюс.)

Теги не проходят через все это. Бирка v2.1 просто имя refs/tags/v2.1. Это означает, что нет способа отделить "их" тег от "вашего" тега. Вы можете иметь либо свой тег или тег. Пока никто не движется тег, это не имеет значения: если вы и тег, он должен указывать на тот же объект. (Если кто-то начинает перемещать теги, все становится уродливым.)

в любом случае Git реализует "нормальную" выборку тегов простым правилом:6когда Git уже имеет фиксацию, если какой-то тег имена that commit, Git копирует тег тоже. с обычными клонами первый клон получает все теги, а затем последующие git fetch операции получить новая теги. Мелкий клон, однако, определение опускает некоторые фиксации, а именно все ниже любой точки трансплантата на графике. Эти коммиты не получат теги. Они!--206-->не могу: чтобы иметь теги, вам нужно будет иметь коммиты. Git не допускается (за исключением мелких трансплантатов) иметь идентификатор фиксации без фактического наличия фиксации.


3вы можете дать git fetch некоторые refspec(ы) в командной строке, и они будут переопределять значение по умолчанию. Это применимо только для выборки по умолчанию. Вы также можете использовать несколько fetch = строки в конфигурации, например, чтобы получить только определенный набор ветвей, хотя обычный способ "де-ограничить" изначально-одинарный клон - это вернуть обычный +refs/heads/*:refs/remotes/origin/* fetch line.

4с тегов должно чтобы двигаться, мы могли бы просто сказать:"это ничего не делает". Но если они двинутся, то ... --200--> в refspec представляет флаг силы, поэтому тег ветров до переезда.

5не делай этого. Это сбивает с толку. Git справится с этим просто отлично-локальная ветвь находится в локальном пространстве имен, а ветвь удаленного отслеживания-в пространстве имен удаленного отслеживания, но это действительно запутанно.

6это правило не соответствует документации. Я протестировал против git версии 2.10.1; старые Gits могут использовать другой метод.


о самом процессе обновления мелкого клона см. совершить 649b0c3 форма Git 2.12 (Q1 2017).
Эта фиксация является частью:

совершить 649b0c3, совершить f2386c6, совершить 6bc3d8c, совершить 0afd307 (06 Dec 2016) by Nguyễn Thái Ng Duc Duy (pclouds). См.совершить 1127b3c, совершить 381aa8e (06 Dec 2016) by Расмус Villemoes (ravi-prevas). (слитый Junio C Hamano -- gitster -- на совершить 3c9979b, 21 дек 2016)

shallow.c

этой paint_down() является частью шага 6 58babff (мелкий.c: 8 шагов для выбора новых коммитов .git / shallow-2013-12-05).
Когда мы получаем из неглубокого репозитория, нам нужно знать, нуждается ли один из новых/обновленных ссылок в новых "неглубоких коммитах" в .git/shallow (потому что у нас недостаточно истории этих refs) и какой из них.

вопрос на шаге 6 заключается в том, какие (новые) мелкие коммиты требуются в другое для поддержания достижимости по всему репозиторию без резка нашей истории короткие?
Чтобы ответить, мы отмечаем все коммиты, доступные из существующих ссылок, неинтересными ("rev-list --not --all"), отметьте мелкие коммиты с дном, затем для каждого нового / обновленного refs пройдите через график фиксации, пока мы не нажмем Неинтересно или снизу, отмечая ref на фиксации, когда мы идем.

после того, как все прогулки сделаны, мы проверяем новые мелкие коммиты. Если мы не видели никакого нового ref, отмеченного на новой мелкой фиксации, мы знаем все новые / обновленные ссылки доступны, используя только нашу историю и .git/shallow.
Мелкий фиксации не нужно и можно выбросить.

Итак, код.

цикл здесь (чтобы пройти через коммиты) в основном:

  1. получить одну фиксацию из очереди
  2. игнорировать, если это видно или неинтересно
  3. пометить его
  4. пройти все родители..
    • 5.a пометить его, если он никогда не отмечался раньше
    • 5.b положите его обратно в очередь

то, что мы делаем в этом патче, - это шаг 5A, потому что это не необходимый.
Маркировка фиксации на 5А ставится обратно в очередь, и будет отмечен на Шаге 3 на следующей итерации. Единственный случай это будет не отмечаться - это когда фиксация уже отмечена неинтересно (5a не проверяет это), который будет проигнорирован на Шаге 2.