В чем различия между git remote prune, git prune, git fetch --prune и т. Д.

360

Моя ситуация такова ... кто-то, работающий с тем же репо, удалил ветку из своего локального и удаленного репо ...

У большинства людей, которые спрашивали об этой проблеме в Stack Overflow или других сайтах, есть проблема с ветвями, которые все еще отображаются в списке удаленных веток отслеживания git branch -aвнизу:

* master
  develop
  feature_blah
  remotes/origin/master
  remotes/origin/develop
  remotes/origin/feature_blah
  remotes/origin/random_branch_I_want_deleted

Однако в МОЕЙ ситуации ветвь, которой там не должно быть, является локальной:

* master
  develop
  feature_blah
  random_branch_I_want_deleted
  remotes/origin/master
  remotes/origin/develop
  remotes/origin/feature_blah

Когда я выполняю любое из следующего, оно не удаляется локально:

$ git prune

Я также попробовал:

$ git remote prune origin
$ git fetch --prune

Более полезная информация: когда я проверяю, git remote show originэто выглядит так:

* remote origin
Fetch URL: utilities:homeconnections_ui.git
Push  URL: utilities:homeconnections_ui.git
HEAD branch: master
Remote branches:
 master                        tracked
 develop                       tracked
 feature_blah                  tracked
 other123                      tracked
 other444                      tracked
 other999                      tracked
Local branches configured for 'git pull':
 develop                      merges with remote develop
 feature_blah                 merges with remote other999
 master                       merges with remote master
 random_branch_I_want_deleted merges with remote random_branch_I_want_deleted
Local refs configured for 'git push':
 develop         pushes to develop     (local out of date)
 master          pushes to master      (up to date)
 feature_blah    pushes to feature_blah(up to date)

Обратите внимание, что это только в разделе под названием Local branches configured for 'git pull':

Почему?

gogogadgetinternet
источник
git branch -d the_local_branch
krsteeve
1
Спасибо, но мне просто интересно, почему это могло произойти.
gogogadgetinternet
При работе с иерархией ветвей ( x/y) была небольшая разница : она была исправлена ​​(см. Мой ответ ниже )
VonC

Ответы:

665

Я не виню вас за то, что вы расстроены из-за этого. Лучший способ посмотреть на это. Существует потенциально три версии каждой удаленной ветви:

  1. Фактическая ветвь в удаленном хранилище
    (например, удаленное хранилище на https://example.com/repo.git , refs/heads/master)
  2. Ваш снимок этой ветки локально (хранится в refs/remotes/...)
    (например, локальное хранилище, refs/remotes/origin/master)
  3. И локальная ветка, которая может отслеживать удаленную ветку
    (например, локальное хранилище, refs/heads/master)

Давайте начнем с git prune. Это удаляет объекты , на которые больше нет ссылок, и не удаляет ссылки. В вашем случае у вас есть местное отделение. Это означает, что есть ссылка с именем, random_branch_I_want_deletedкоторая относится к некоторым объектам, которые представляют историю этой ветви. Так что по определению git pruneне буду удалять random_branch_I_want_deleted. Действительно, git pruneэто способ удалить данные, которые накопились в Git, но на которые ничего не ссылаются. В целом, это не влияет на ваш взгляд на какие-либо ветви.

git remote prune originи git fetch --pruneоба оперируют ссылками в разделе refs/remotes/...(я буду называть их удаленными ссылками). Это не влияет на местные филиалы. git remoteВерсия полезна , если вы хотите , чтобы удалить удаленные ссылки под конкретный пульт ДУ. В противном случае эти двое делают одно и то же. Итак, короче, git remote pruneи git fetch --pruneоперируй на номер 2 выше. Например, если вы удалили ветку с помощью веб-интерфейса git и больше не хотите, чтобы она отображалась в вашем локальном списке ветвей ( git branch -r), то эту команду вам следует использовать.

Чтобы удалить локальную ветку, вы должны использовать git branch -d(или, -Dесли она нигде не объединена). FWIW, нет команды git для автоматического удаления локальных ветвей отслеживания, если удаленная ветвь исчезает.

Джон Шакмейстер
источник
22
Это делает лучшую работу по решению общего вопроса путем объяснения соответствующих различий. Он также отвечает на дополнительные вопросы, которые у меня были от одного выше.
gogogadgetinternet
14
Эта команда покажет список всех локальных веток, которые не имеют соответствующей удаленной ветки. Вы можете передать это xargs git branch -D, но учтите, что любые новые ветки, которые вы создали, но никогда не отправляли на сервер, будут удалены, поэтому действуйте осторожно: git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}'
Jason Walton
4
@ Семя Нет, это не так. :-( Это только удаляет ссылки на локальное удаленное отслеживание. Я только что проверил это с версией 2.7.0.
Джон Szakmeister
1
@ BlueRaja-DannyPflughoeft Будьте осторожны с таким подходом. Например, в зависимости от того, как вы работаете со своими стабильными ветвями, они могут казаться объединенными в основную ветку, и вы в конечном итоге удалите их. Это не большая потеря, поскольку вы не удаляете их с сервера, но если у вас была какая-то специальная конфигурация, которую вы для нее установили, она будет потеряна при удалении ветки.
Джон Szakmeister
1
@ Cloud Не совсем верно. Ссылки могут быть упакованы (см. packed-refsФайл в этой .gitобласти), поэтому не обязательно просто удалить их через проводник. Лучше использовать команды, чтобы убедиться, что оба позаботились правильно.
Джон Szakmeister
55

git remote pruneи git fetch --pruneсделайте то же самое: удалите ссылки на несуществующие ветви, как вы сказали. Вторая команда подключается к удаленному и выбирает его текущие ветви перед удалением.

Однако это не затрагивает местные филиалы, которые вы проверили, которые вы можете просто удалить с помощью

git branch -d  random_branch_I_want_deleted

Заменить -dна, -Dесли ветка не объединена в другом месте

git prune делает что-то другое, он очищает недоступные объекты, те коммиты, которые недоступны ни в одной ветви или теге, и, следовательно, больше не нужны.

CharlesB
источник
1
Я знаю, это кажется очевидным, но git pruneищет не только ветки и теги, но и все другие ссылки.
Так что в моем случае, почему бы не работать с git prune? Потому что это не заботится о локальных филиалах, но удаленные ссылки? Спасибо за краткую информацию.
gogogadgetinternet
@hvd Что за ссылки, кроме веток и тегов?
CharlesB
@gogogadgetinternet да точно. (предположим, вы имели в виду git remote prune)
CharlesB
4
IMO Соглашение об именах git об использовании «prune» как для сбора объектов, так и для очистки ссылок - вот где возникает путаница. Но это только одна из многих головоломок пользовательского интерфейса в git. :-)
torek
14

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

Если вы, ребята, видите какие-либо проблемы с этим, пожалуйста, дайте мне знать, и я исправлю это (и т.д. и т. Д.)

Сохраните его в файле с именем git-rm-ntb(называйте как угодно) PATHи запустите:

git-rm-ntb <remote1:optional> <remote2:optional> ...

clean()
{
  REMOTES="$@";
  if [ -z "$REMOTES" ]; then
    REMOTES=$(git remote);
  fi
  REMOTES=$(echo "$REMOTES" | xargs -n1 echo)
  RBRANCHES=()
  while read REMOTE; do
    CURRBRANCHES=($(git ls-remote $REMOTE | awk '{print $2}' | grep 'refs/heads/' | sed 's:refs/heads/::'))
    RBRANCHES=("${CURRBRANCHES[@]}" "${RBRANCHES[@]}")
  done < <(echo "$REMOTES" )
  [[ $RBRANCHES ]] || exit
  LBRANCHES=($(git branch | sed 's:\*::' | awk '{print $1}'))
  for i in "${LBRANCHES[@]}"; do
    skip=
    for j in "${RBRANCHES[@]}"; do
      [[ $i == $j ]] && { skip=1; echo -e "\033[32m Keeping $i \033[0m"; break; }
    done
    [[ -n $skip ]] || { echo -e "\033[31m $(git branch -D $i) \033[0m"; }
  done
}

clean $@
D.Mill
источник
Спасибо!
Приятное
2
Не безопаснее ли $ (git branch -d $ i) удалять только объединенные ветви?
user2012677
Так полезно, спасибо большое !!!
Робин Хартленд
Более безопасные варианты обсуждаются в stackoverflow.com/questions/7726949/…
Майкл Фрейдгейм
13

Следует отметить , что одно различие между git remote --pruneи git fetch --pruneв настоящее время фиксируется с фиксацией 10a6cc8 , от Тома Миллера ( tmiller) (для мерзавца 1.9 / 2.0, Q1 2014):

Когда у нас есть ветвь удаленного отслеживания с именем " frotz/nitfol" из предыдущей выборки, и у восходящего потока теперь есть ветвь с именем "** frotz " **, fetchне удастся удалить " frotz/nitfol" с помощью " git fetch --prune" из восходящего потока.
git сообщит пользователю, что нужно использовать « git remote prune» для решения проблемы.

Итак: когда в восходящем репо есть ветвь («frotz») с тем же именем, что и в иерархии ветвей («frotz / xxx», возможное соглашение об именах ветвей ), это git remote --pruneбыло успешно (при очистке удаленной ветки отслеживания из вашего репо) , но git fetch --pruneтерпел неудачу.

Уже нет:

Измените способ " fetch --prune", перемещая операцию отсечения перед операцией извлечения.
Таким образом, вместо предупреждения пользователя о конфликте, он автоматически исправляет его.

VonC
источник