Sự khác biệt giữa git remote prune, git prune, git fetch --prune, v.v.


358

Tình huống của tôi là ... ai đó làm việc trên cùng một repo đã xóa một chi nhánh khỏi repo địa phương và từ xa của mình ...

Hầu hết những người đã hỏi về loại vấn đề này trên Stack Overflow hoặc các trang web khác có vấn đề về các chi nhánh vẫn hiển thị trong danh sách chi nhánh theo dõi từ xa của họ git branch -aở phía dưới:

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

Tuy nhiên, trong tình huống của tôi, chi nhánh không nên ở đó, là địa phương:

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

Khi tôi thực hiện bất kỳ thao tác nào sau đây, nó sẽ không bị xóa cục bộ:

$ git prune

Tôi cũng đã thử:

$ git remote prune origin
$ git fetch --prune

Thông tin hữu ích khác: Khi tôi kiểm tra xem git remote show originnó trông như thế nào:

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

Lưu ý rằng nó chỉ trong phần có tiêu đề Local branches configured for 'git pull':

Tại sao?


git branch -d the_local_branch
krsteeve

1
Cảm ơn, nhưng tôi chỉ tò mò về lý do tại sao nó có thể xảy ra.
gogogadgeti Internet

Có một sự khác biệt tinh tế khi xử lý phân cấp nhánh ( x/y): nó đã được sửa (xem câu trả lời của tôi bên dưới )
VonC

Câu trả lời:


664

Tôi không trách bạn đã thất vọng về điều này. Cách tốt nhất để xem xét là đây. Có khả năng có ba phiên bản của mỗi chi nhánh từ xa:

  1. Chi nhánh thực tế trên kho lưu trữ từ xa
    (ví dụ: repo từ xa tại https://example.com/repo.git , refs/heads/master)
  2. Ảnh chụp nhanh của bạn về chi nhánh đó (được lưu trữ bên dưới refs/remotes/...)
    (ví dụ: repo cục bộ, refs/remotes/origin/master)
  3. Và một nhánh cục bộ có thể đang theo dõi nhánh từ xa
    (ví dụ: repo cục bộ, refs/heads/master)

Hãy bắt đầu với git prune. Điều này loại bỏ các đối tượng không còn được tham chiếu, nó không xóa tham chiếu. Trong trường hợp của bạn, bạn có một chi nhánh địa phương. Điều đó có nghĩa là có một tham chiếu có tên random_branch_I_want_deletedđề cập đến một số đối tượng đại diện cho lịch sử của chi nhánh đó. Vì vậy, theo định nghĩa, git prunesẽ không loại bỏ random_branch_I_want_deleted. Thực sự, git prunelà một cách để xóa dữ liệu đã tích lũy trong Git nhưng không được tham chiếu bởi bất cứ điều gì. Nói chung, nó không ảnh hưởng đến quan điểm của bạn về bất kỳ chi nhánh nào.

git remote prune origingit fetch --prunecả hai đều hoạt động trên các tham chiếu bên dưới refs/remotes/...(Tôi sẽ gọi chúng là các tham chiếu từ xa). Nó không ảnh hưởng đến các chi nhánh địa phương. Các git remotephiên bản rất hữu ích nếu bạn chỉ muốn loại bỏ các tham chiếu từ xa dưới một từ xa nói riêng. Nếu không, cả hai làm chính xác cùng một điều. Vì vậy, trong ngắn hạn, git remote prunegit fetch --prunehoạt động trên số 2 ở trên. Ví dụ: nếu bạn đã xóa một nhánh bằng GUI web git và không muốn nó hiển thị trong danh sách nhánh cục bộ của bạn nữa ( git branch -r), thì đây là lệnh bạn nên sử dụng.

Để xóa một nhánh cục bộ, bạn nên sử dụng git branch -d(hoặc -Dnếu nó không được hợp nhất ở bất cứ đâu). FWIW, không có lệnh git để tự động xóa các nhánh theo dõi cục bộ nếu một nhánh từ xa biến mất.


22
Đây là một công việc tốt hơn để giải quyết câu hỏi tổng thể bằng cách giải thích sự khác biệt thích hợp. Nó cũng trả lời các câu hỏi bổ sung mà tôi có từ câu hỏi trên.
gogogadgeti Internet

14
Lệnh này sẽ hiển thị danh sách tất cả các nhánh cục bộ không có nhánh từ xa tương ứng. Bạn có thể cái này sang xargs git branch -D, nhưng lưu ý rằng bất kỳ nhánh mới nào bạn đã tạo nhưng chưa bao giờ được đẩy lên máy chủ sẽ bị xóa, vì vậy hãy cẩn thận: git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}'
Jason Walton

4
@Seed Không, nó không. :-( Nó chỉ xóa các ref theo dõi từ xa cục bộ. Tôi chỉ kiểm tra kỹ cái này với phiên bản 2.7.0.
John Szakmeister

1
@ BlueRaja-DannyPflughoeft Hãy cẩn thận với cách tiếp cận đó. Chẳng hạn, tùy thuộc vào cách bạn thực hiện các nhánh ổn định của mình, chúng có thể được sáp nhập vào nhánh chính và cuối cùng bạn sẽ loại bỏ chúng. Đây không phải là một mất mát lớn vì bạn không xóa chúng khỏi máy chủ, nhưng nếu bạn có bất kỳ cấu hình đặc biệt nào bạn đặt cho nó thì điều đó sẽ bị mất khi chi nhánh bị xóa.
John Szakmeister

1
@Cloud Không hoàn toàn đúng. Tài liệu tham khảo có thể được đóng gói (xem packed-refstệp trong .gitkhu vực), do đó, không nhất thiết phải xóa chúng thông qua trình duyệt tệp. Tốt hơn là sử dụng các lệnh để đảm bảo cả hai được chăm sóc chính xác.
John Szakmeister

55

git remote prunegit fetch --prunelàm điều tương tự: xóa các ref cho các nhánh không tồn tại trên remote, như bạn đã nói. Lệnh thứ hai kết nối với điều khiển từ xa và tìm nạp các nhánh hiện tại của nó trước khi cắt tỉa.

Tuy nhiên, nó không chạm vào các chi nhánh địa phương mà bạn đã kiểm tra, mà bạn có thể chỉ cần xóa bằng

git branch -d  random_branch_I_want_deleted

Thay thế -dbằng -Dnếu chi nhánh không được hợp nhất ở nơi khác

git prune làm một cái gì đó khác nhau, nó thanh trừng các đối tượng không thể truy cập, những cam kết không thể truy cập trong bất kỳ chi nhánh hoặc thẻ nào, và do đó không cần thiết nữa.


1
Tôi biết nó có vẻ rõ ràng, nhưng git prunetrông không chỉ cho các chi nhánh và thẻ, mà tất cả các ref khác.

Vì vậy, trong trường hợp của tôi, tại sao git prune làm việc? Bởi vì nó không quan tâm đến các chi nhánh địa phương, nhưng các tài liệu tham khảo từ xa? Cảm ơn thông tin ngắn gọn.
gogogadgeti Internet

@hvd loại ref nào khác ngoài nhánh và thẻ?
CharlesB

@gogogadgeti Internet có chính xác. (giả sử bạn có ý git remote prune)
CharlesB

4
IMO quy ước đặt tên git của việc sử dụng "prune" cho cả thu thập đối tượng dọn dẹp tham chiếu là nơi gây nhầm lẫn. Nhưng đó chỉ là một trong nhiều bối rối UI, trong git. :-)
torek

14

Trong trường hợp bất cứ ai cũng sẽ quan tâm. Đây là tập lệnh shell nhanh sẽ loại bỏ tất cả các nhánh cục bộ không được theo dõi từ xa. Một lời cảnh báo: Điều này sẽ loại bỏ bất kỳ chi nhánh nào không được theo dõi từ xa bất kể nó có được hợp nhất hay không.

Nếu các bạn thấy bất kỳ vấn đề nào với điều này, xin vui lòng cho tôi biết và tôi sẽ sửa nó (v.v.)

Lưu nó trong một tệp gọi là git-rm-ntb(gọi nó là bất cứ thứ gì) trên PATHvà chạy:

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 $@

Cảm ơn! Liên lạc tốt đẹp với đầu ra màu ;-)
BVengerov

2
$ (Git chi nhánh -d $ i) sẽ không an toàn hơn khi chỉ xóa các chi nhánh được hợp nhất?
user2012677

Rất hữu ích cảm ơn bạn rất nhiều !!!
Robin Hartland

Các tùy chọn an toàn hơn được thảo luận trong stackoverflow.com/questions/7726949/
Khăn

13

Lưu ý rằng một sự khác biệt giữa git remote --prunegit fetch --pruneđang được khắc phục, với cam kết 10a6cc8 , bởi Tom Miller ( tmiller) (đối với git 1.9 / 2.0, Q1 2014):

Khi chúng ta có một nhánh theo dõi từ xa có tên " frotz/nitfol" từ lần tìm nạp trước đó và hiện tại thượng nguồn có một nhánh có tên "** frotz " **, fetchsẽ không thể xóa " frotz/nitfol" bằng " git fetch --prune" từ thượng nguồn.
git sẽ thông báo cho người dùng sử dụng " git remote prune" để khắc phục sự cố.

Vì vậy: khi repo ngược dòng có một nhánh ("frotz") có cùng tên với phân cấp nhánh ("frotz / xxx", một quy ước đặt tên nhánh có thể ), git remote --pruneđã thành công (trong việc dọn sạch nhánh theo dõi từ xa từ repo của bạn) , nhưng git fetch --pruneđã thất bại.

Không còn nữa:

Thay đổi cách " fetch --prune" hoạt động bằng cách di chuyển thao tác cắt tỉa trước khi thao tác tìm nạp.
Bằng cách này, thay vì cảnh báo người dùng về xung đột, nó sẽ tự động sửa nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.