Как уменьшить глубину существующего клона git?

У меня есть клон. Я хочу уменьшить историю на нем, без клонирования с нуля с уменьшенной глубиной. Пример работы:

$ git clone [email protected]:apache/spark.git
# ...
$ cd spark/
$ du -hs .git
193M    .git

Хорошо, так что не так, но, но это послужит для этого обсуждения. Если я попробую gc, он станет меньше:

$ git gc --aggressive
Counting objects: 380616, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (278136/278136), done.
Writing objects: 100% (380616/380616), done.
Total 380616 (delta 182748), reused 192702 (delta 0)
Checking connectivity: 380616, done.
$ du -hs .git
108M    .git

Тем не менее, довольно большой (git pull предполагает, что он все еще нажимается/снимается на пульте дистанционного управления). Как насчет repack?

$ git repack -a -d --depth=5
Counting objects: 380616, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (95388/95388), done.
Writing objects: 100% (380616/380616), done.
Total 380616 (delta 182748), reused 380616 (delta 182748)
Pauls-MBA:spark paul$ du -hs .git
108M    .git

Yup, не стал меньше. --depth для repack для клона не тождественно:

$ git clone --depth 1 [email protected]:apache/spark.git
Cloning into 'spark'...
remote: Counting objects: 8520, done.
remote: Compressing objects: 100% (6611/6611), done.
remote: Total 8520 (delta 1448), reused 5101 (delta 710), pack-reused 0
Receiving objects: 100% (8520/8520), 14.82 MiB | 3.63 MiB/s, done.
Resolving deltas: 100% (1448/1448), done.
Checking connectivity... done.
Checking out files: 100% (13386/13386), done.
$ cd spark
$ du -hs .git
17M .git

Git pull говорит, что он все еще находится на шаг с пультом, что никого не удивляет.

ОК - так как изменить существующий клон на мелкий клон, не прикрепили его и не заново заново забыли?

Ответ 1

git clone --mirror --depth=5  file://$PWD ../temp
rm -rf .git/objects
mv ../temp/{shallow,objects} .git
rm -rf ../temp

Это действительно не клонирование "с нуля", поскольку это чисто местная работа, и оно создает практически ничего, кроме файлов с закрытыми пакетами, вероятно, всего в десятках килобайт. Я бы рискнул, что вы не получите более эффективного, чем это, вы завершаете работу, которая использует больше места в виде скриптов и тестовой работы, чем это делает в виде нескольких kb временных накладных расходов РЕПО.

Ответ 2

так как минимум git версии 2.14.1 есть

git fetch --depth 10

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

обрезанные коммиты больше не будут доступны обычным способом, но все еще будут задерживаться в хранилище (reflog). если нет других ссылок, удерживающих их, то они будут в конечном итоге удалены автоматически git gc.

Вы также можете немедленно удалить старые коммиты. Для этого вы должны удалить все ссылки, которые могут их содержать. это в основном рефлог и теги. затем запустите git gc.

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

если вы удалили теги, то при следующей git fetch выборке будут только те теги для коммитов, которые в данный момент находятся в репозитории.

очистить журнал:

git reflog expire --expire=all --all

удалить все теги:

git tag -l | xargs git tag -d

удалить все висящие объекты:

git gc --prune=all

Ответ 3

Изменить, февраль 2017: этот ответ устарел/ошибочен. Git может сделать мелкий клон более мелким, по крайней мере внутренне. Git 2.11 также имеет --deepen, чтобы увеличить глубину клона, и похоже, что есть возможные планы разрешить отрицательные значения (хотя сейчас они отклонены). Неясно, насколько хорошо это работает в реальном мире, и ваш лучший выбор - это клонировать клон, как в jthill answer.


Вы можете только углубить репозиторий. Это связано прежде всего с тем, что Git создается вокруг добавления нового материала. Путь к мелким клонам заключается в том, что ваш (получающий) Git получает отправителя (другой Git), чтобы прекратить отправку "нового материала" при достижении аргумента глубины-клонирования и координаты с отправителем, чтобы понять, почему они остановились в этот момент, хотя, очевидно, требуется больше истории. Затем они записывают идентификаторы "усеченных" коммитов в специальный файл .git/shallow, который обе помещает репозиторий как неглубокий, а заметки, которые совершают, усекаются.

Обратите внимание, что во время этого процесса ваш Git все еще добавляет новые вещи. (Кроме того, когда он завершил клонирование и выходы, Git забыл, что такое глубина, и со временем становится невозможным даже выяснить, что это такое. Все Git могут сказать, что это мелкий клон, потому что .git/shallow файл, содержащий идентификаторы фиксации, все еще существует.)

Остальная часть Git продолжает строиться вокруг этой концепции "добавить новый материал", поэтому вы можете углубить клон, но не увеличивать его поверхностность. (Нет ничего хорошего, согласованного глагола для этого: противоположность углублению ямы заполняет его, но заполнение имеет неправильную коннотацию. Diminish может работать, я думаю, что я буду использовать это.)

В теории git gc, которая является единственной частью Git, которая когда-либо фактически выкидывает что-либо, 1 может, возможно, уменьшить репозиторий, даже превращая полный клон в мелкий, но никто не написал для этого код. Есть несколько сложных битов, например, вы отбрасываете теги? Неглубокие клоны начинают использовать теги sans по причинам реализации, поэтому преобразование репозитория в неглубокое или уменьшение существующего мелкого хранилища может потребовать отбрасывания хотя бы некоторых тегов. Конечно, любой тег, указывающий на фиксацию, уничтоженную уменьшающимся действием, должен уйти.


Между тем аргумент --depth для git-pack-objects (прошедший через git repack) означает что-то другое: максимальная длина дельта-цепочки, когда Git использует свой модифицированный xdelta compression на Git объекты, хранящиеся в каждом файле pack. Это не имеет никакого отношения к глубине отдельных частей DAG фиксации (как вычислено из каждой ветки ветки).


1 Ну, git repack завершает выброс вещей как побочный эффект, в зависимости от того, какие флаги используются, но он вызвал этот путь на git gc. Это справедливо и для git prune. Чтобы эти две команды действительно выполняли свою работу должным образом, им нужно git reflog expire запустить сначала. "Обычный пользовательский" конец последовательности "чистая" - git gc; он имеет дело со всем этим. Поэтому мы можем сказать, что git gc - это то, как вы отбрасываете накопленный "новый материал", который оказался нежелательным в конце концов.

Ответ 4

ОК, вот попытка bash it, которая игнорирует ветки, не относящиеся к умолчанию, а также предполагается, что пульт называется "origin":

#!/bin/sh

set -e

mkdir .git_slimmer

cd $1

changed_lines=$(git status --porcelain | wc -l)
ahead_of_remote=$(git status | grep "Your branch is ahead" | wc -l)
remote_url=$(git remote show origin  | grep Fetch | cut -d' ' -f5)
latest_sha=$(git log | head -n 1 | cut -d' ' -f2)

cd ..

if [ "$changed_lines" -gt "0" ]
then
  echo "Untracked Changes - won't make the clone slimmer in that situation"
  exit 1
fi

if [ "$ahead_of_remote" -gt "0" ]
then
  echo "Local commits not in the remote - won't make the clone slimmer in that situation"
  exit 1
fi

cd .git_slimmer
git clone $remote_url --no-checkout --depth 1 foo
cd foo
latest_sha_for_new=$(git log | head -n 1 | cut -d' ' -f2)
cd ../..

if [ "$latest_sha" == "$latest_sha_for_new" ]
then
  mv "$1/.git" "$1/.gitOLD"
  mv ".git_slimmer/foo/.git" "$1/"
  rm -rf "$1/.gitOLD"
  cd "$1"
  git add .
  cd ..
else
  echo "SHA from head of existing get clone does not match the latest one from the remote: do a git pull first"
  exit 1
fi

rm -rf .git_slimmer

Использование: 'git -slimmer.sh <folder_containing_git_repo > '