Apa perbedaan antara git prune remote, git prune, git fetch --prune, dll

358

Situasi saya adalah ini ... seseorang yang mengerjakan repo yang sama telah menghapus cabang dari repo lokal & jauh ...

Sebagian besar orang yang bertanya tentang masalah seperti ini di Stack Overflow, atau situs lain memiliki masalah cabang yang masih ditampilkan di daftar cabang pelacakan jarak jauh mereka git branch -adi bagian bawah:

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

Namun, dalam situasi SAYA cabang yang seharusnya tidak ada, adalah lokal:

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

Ketika saya melakukan salah satu dari yang berikut ini, itu tidak bisa dihapus secara lokal:

$ git prune

Saya juga mencoba:

$ git remote prune origin
$ git fetch --prune

Info lebih berguna: Ketika saya memeriksa git remote show originini tampilannya:

* 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)

Perhatikan bahwa ini hanya ada di bagian berjudul Local branches configured for 'git pull':

Mengapa?

gogogadgetinternet
sumber
git branch -d the_local_branch
krsteeve
1
Terima kasih, tapi saya hanya ingin tahu mengapa itu terjadi.
gogogadgetinternet
Ada perbedaan tipis ketika berhadapan dengan hierarki cabang ( x/y): sudah diperbaiki (lihat jawaban saya di bawah )
VonC

Jawaban:

665

Saya tidak menyalahkan Anda karena frustrasi tentang hal ini. Cara terbaik untuk melihatnya adalah ini. Ada berpotensi tiga versi dari setiap cabang jarak jauh:

  1. Cabang aktual pada repositori jarak jauh
    (mis. Repo jarak jauh di https://example.com/repo.git , refs/heads/master)
  2. Cuplikan Anda dari cabang itu secara lokal (disimpan di bawah refs/remotes/...)
    (mis. Repo lokal, refs/remotes/origin/master)
  3. Dan cabang lokal yang mungkin melacak cabang jarak jauh
    (misalnya, repo lokal, refs/heads/master)

Mari kita mulai git prune. Ini menghapus objek yang tidak lagi direferensikan, itu tidak menghapus referensi. Dalam kasus Anda, Anda memiliki cabang lokal. Itu berarti ada seorang wasit bernama random_branch_I_want_deletedyang merujuk pada beberapa objek yang mewakili sejarah cabang itu. Jadi, menurut definisi, git prunetidak akan menghapus random_branch_I_want_deleted. Sungguh, git pruneadalah cara untuk menghapus data yang telah terakumulasi di Git tetapi tidak dirujuk oleh apa pun. Secara umum, itu tidak mempengaruhi pandangan Anda tentang cabang apa pun.

git remote prune origindan git fetch --prunekeduanya beroperasi pada referensi di bawah refs/remotes/...(saya akan merujuk ini sebagai referensi jarak jauh). Itu tidak mempengaruhi cabang lokal. The git remoteVersi ini berguna jika Anda hanya ingin menghapus referensi terpencil di bawah remote tertentu. Kalau tidak, keduanya melakukan hal yang persis sama. Jadi, singkatnya, git remote prunedan git fetch --pruneberoperasi pada nomor 2 di atas. Misalnya, jika Anda menghapus cabang menggunakan GUI web GUI dan tidak ingin itu muncul di daftar cabang lokal Anda lagi ( git branch -r), maka ini adalah perintah yang harus Anda gunakan.

Untuk menghapus cabang lokal, Anda harus menggunakan git branch -d(atau -Djika tidak digabung di mana saja). FWIW, tidak ada perintah git untuk secara otomatis menghapus cabang pelacakan lokal jika cabang jarak jauh menghilang.

John Szakmeister
sumber
22
Ini melakukan pekerjaan yang lebih baik untuk menjawab pertanyaan keseluruhan dengan menjelaskan perbedaan yang terkait. Itu juga menjawab pertanyaan tambahan yang saya miliki dari yang di atas.
gogogadgetinternet
14
Perintah ini akan menampilkan daftar semua cabang lokal yang tidak memiliki cabang jarak jauh yang sesuai. Anda dapat mem - pipe ini xargs git branch -D, tetapi perhatikan bahwa setiap cabang baru yang Anda buat tetapi tidak pernah didorong ke server akan dihapus, jadi injak dengan hati-hati: git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}'
Jason Walton
4
@Kata Tidak, tidak. :-( Ini hanya menghapus referensi pelacakan jarak jauh lokal. Saya hanya memeriksa ulang ini dengan versi 2.7.0.
John Szakmeister
1
@ BlueRaja-DannyPflughoeft Hati-hati dengan pendekatan itu. Misalnya, tergantung pada bagaimana Anda melakukan cabang stabil Anda, mereka mungkin tampaknya digabungkan ke cabang master, dan Anda akhirnya akan menghapusnya. Ini bukan kerugian besar di sini karena Anda tidak menghapusnya dari server, tetapi jika Anda memiliki konfigurasi khusus yang Anda tetapkan untuk itu maka itu akan hilang ketika cabang dihapus.
John Szakmeister
1
@Cloud Tidak sepenuhnya benar. Referensi dapat dikemas (lihat packed-refsfile di .gitarea), jadi itu tidak selalu masalah sederhana menghapusnya melalui file explorer. Lebih baik menggunakan perintah untuk memastikan keduanya diurus dengan benar.
John Szakmeister
55

git remote prunedan git fetch --prunelakukan hal yang sama: menghapus referensi ke cabang yang tidak ada pada remote, seperti yang Anda katakan. Perintah kedua terhubung ke remote dan mengambil cabang-cabangnya saat ini sebelum pemangkasan.

Namun itu tidak menyentuh cabang lokal yang sudah Anda periksa, yang bisa Anda hapus dengan

git branch -d  random_branch_I_want_deleted

Ganti -ddengan -Djika cabang tidak digabung di tempat lain

git prune melakukan sesuatu yang berbeda, membersihkan objek yang tidak terjangkau, komit yang tidak dapat dijangkau di cabang atau tag apa pun, dan karenanya tidak diperlukan lagi.

CharlesB
sumber
1
Saya tahu itu tampak jelas, tetapi git prunetidak hanya mencari cabang dan tag, tetapi semua referensi lainnya juga.
Jadi dalam kasus saya, mengapa git pangkas tidak berfungsi? Karena tidak peduli dengan cabang lokal, tetapi referensi jarak jauh? Terima kasih atas info singkatnya.
gogogadgetinternet
@ Hvd referensi macam apa yang ada selain cabang dan tag?
CharlesB
@ gogogadgetinternet ya persisnya. (Seandainya maksudmu git remote prune)
CharlesB
4
IMO konvensi penamaan git menggunakan "prune" untuk pengumpulan-objek dan pembersihan-referensi adalah tempat kebingungan muncul. Tapi itu hanya salah satu dari banyak kebingungan UI, di git. :-)
torek
14

Jika ada orang yang tertarik. Berikut skrip shell cepat yang akan menghapus semua cabang lokal yang tidak dilacak dari jarak jauh. Peringatan: Ini akan menyingkirkan cabang apa pun yang tidak dilacak dari jarak jauh terlepas dari apakah itu digabung atau tidak.

Jika kalian melihat ada masalah dengan ini, beri tahu saya dan saya akan memperbaikinya (dll.)

Simpan di file bernama git-rm-ntb(panggil saja) PATHdan jalankan:

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. Pabrik
sumber
Terima kasih! Sentuhan yang bagus dengan output berwarna ;-)
BVengerov
2
Bukankah $ (git branch -d $ i) lebih aman untuk hanya menghapus cabang yang digabung?
user2012677
Sangat membantu, terima kasih banyak !!!
Robin Hartland
Opsi yang lebih aman dibahas di stackoverflow.com/questions/7726949/…
Michael Freidgeim
13

Perhatikan bahwa satu perbedaan antara git remote --prunedan git fetch --prunesedang diperbaiki, dengan commit 10a6cc8 , oleh Tom Miller ( tmiller) (untuk git 1.9 / 2.0, Q1 2014):

Ketika kami memiliki cabang pelacakan jarak jauh bernama " frotz/nitfol" dari pengambilan sebelumnya, dan hulu sekarang memiliki cabang bernama "** frotz " **, fetchakan gagal menghapus " frotz/nitfol" dengan " git fetch --prune" dari hulu.
git akan memberitahu pengguna untuk menggunakan " git remote prune" masalah ini.

Jadi: ketika repo hulu memiliki cabang ("frotz") dengan nama yang sama dengan hierarki cabang ("frotz / xxx", kemungkinan konvensi penamaan cabang ), git remote --pruneberhasil (dalam membersihkan cabang pelacakan jarak jauh dari repo Anda) , tetapi git fetch --prunegagal.

Tidak lagi:

Ubah cara fetch --prunekerjanya " " dengan memindahkan operasi pemangkasan sebelum operasi pengambilan.
Dengan cara ini, alih-alih memperingatkan pengguna konflik, secara otomatis memperbaikinya.

VONC
sumber