Xóa các nhánh theo dõi không còn trên remote


1173

Có cách nào đơn giản để xóa tất cả các nhánh theo dõi mà tương đương từ xa không còn tồn tại không?

Thí dụ:

Chi nhánh (địa phương và từ xa)

  • bậc thầy
  • nguồn gốc / chủ
  • nguồn gốc / sửa lỗi-a
  • nguồn gốc / sửa lỗi-b
  • nguồn gốc / sửa lỗi-c

Ở địa phương, tôi chỉ có một chi nhánh chính. Bây giờ tôi cần phải làm việc với bug-fix-a , vì vậy tôi kiểm tra nó, làm việc với nó và đẩy các thay đổi vào điều khiển từ xa. Tiếp theo tôi làm tương tự với bug-fix-b .

Chi nhánh (địa phương và từ xa)

  • bậc thầy
  • sửa lỗi
  • sửa lỗi-b
  • nguồn gốc / chủ
  • nguồn gốc / sửa lỗi-a
  • nguồn gốc / sửa lỗi-b
  • nguồn gốc / sửa lỗi-c

Bây giờ tôi có chủ chi nhánh địa phương , bug-fix-a , bug-fix-b . Trình duy trì nhánh Master sẽ hợp nhất các thay đổi của tôi thành master và xóa tất cả các nhánh mà anh ta đã hợp nhất.

Vì vậy, trạng thái hiện tại là:

Chi nhánh (địa phương và từ xa)

  • bậc thầy
  • sửa lỗi
  • sửa lỗi-b
  • nguồn gốc / chủ
  • nguồn gốc / sửa lỗi-c

Bây giờ tôi muốn gọi một số lệnh để xóa các nhánh (trong trường hợp này là bug-fix-a , bug-fix-b ), không còn được biểu diễn trong kho lưu trữ từ xa.

Nó sẽ giống như lệnh hiện có git remote prune origin, nhưng giống hơn git local prune origin.

Câu trả lời:


1282

git remote prune origin cắt tỉa theo dõi các chi nhánh không phải từ xa.

git branch --merged liệt kê các chi nhánh đã được sáp nhập vào chi nhánh hiện tại.

xargs git branch -d xóa các nhánh được liệt kê trên đầu vào tiêu chuẩn.

Hãy cẩn thận xóa các chi nhánh được liệt kê bởi git branch --merged. Danh sách có thể bao gồm masterhoặc các chi nhánh khác bạn không muốn xóa.

Để cho mình cơ hội chỉnh sửa danh sách trước khi xóa các nhánh, bạn có thể thực hiện các thao tác sau trong một dòng:

git branch --merged >/tmp/merged-branches && \
  vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches

17
Dòng đầu tiên của các chi nhánh được sáp nhập là * mastertrên hệ thống của tôi. Lệnh sau có tác dụng với tôi:git branch -d $(git branch --merged |tail -n +2)
Trendfischer

99
Nếu tôi vào developthì git branch --mergedbao gồm master! Bạn có thể (chắc chắn!) Không muốn xóa điều đó. Ngoài ra tôi nghĩ nó nên là git branch -dchữ thường -dcó nghĩa là "xóa an toàn", ví dụ như chỉ xóa nếu được hợp nhất.
thom_nic

11
Có vẻ như một giải pháp cải tiến được cung cấp ở đó .
Sergey Brunov

34
Loại bỏ sáp nhập là hữu ích, nhưng không giống như "loại bỏ các chi nhánh không phải từ xa."
dlsso

11
Chỉ cần sử dụng grep để loại trừ chủ:git branch --merged | grep -v "master" >/tmp/merged-branches && vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches
geniass

591

Sau lệnh

git fetch -p

xóa các tham chiếu từ xa, khi bạn chạy

git branch -vv

nó sẽ hiển thị 'biến mất' dưới dạng trạng thái từ xa. Ví dụ,

$ git branch -vv
  master                 b900de9 [origin/master: behind 4] Fixed bug
  release/v3.8           fdd2f4e [origin/release/v3.8: behind 2] Fixed bug
  release/v3.9           0d680d0 [origin/release/v3.9: behind 2] Updated comments
  bug/1234               57379e4 [origin/bug/1234: gone] Fixed bug

Vì vậy, bạn có thể viết một tập lệnh đơn giản để loại bỏ các nhánh cục bộ đã đi từ xa:

git fetch -p && for branch in $(git branch -vv | grep ': gone]' | awk '{print $1}'); do git branch -D $branch; done

Lưu ý rằng ở trên sử dụng lệnh "sứ" git branchđể có được trạng thái ngược dòng.

Một cách khác để có được trạng thái này là sử dụng lệnh "ống nước" git for-each-refvới biến nội suy %(upstream:track), sẽ [gone]giống như trên.

Cách tiếp cận này có phần an toàn hơn, vì không có nguy cơ vô tình khớp trên một phần của thông điệp cam kết.

git fetch -p && for branch in $(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}'); do git branch -D $branch; done

8
@KrzysztofWende - không phải trên Solaris và một số BSD và một số OS X :)
jww

4
Dường như điều này cũng sẽ loại bỏ bất kỳ chi nhánh nào đã "biến mất" trong thông điệp cam kết cuối cùng.
dlsso

11
@dlsso Nếu thông báo cam kết cuối cùng chứa chuỗi ": gone]" thì có, nó cũng sẽ bị xóa. Bạn có thể làm cho nó mạnh mẽ hơn với chi phí đơn giản bằng cách có thêm awk / gawk để loại bỏ thông điệp cam kết. git branch -vv | gawk '{print $1,$4}' | grep 'gone]' | gawk '{print $1}'
jason.rickman

2
Trả lời nhận xét trước đây của tôi (câu hỏi), chi nhánh hiện tại có * là trường đầu tiên. Nếu nó cũng nằm trong danh sách các nhánh "đã biến mất", $ 1 sẽ được chỉ định * và sẽ được hiểu là một tệp tin với việc loại bỏ tên tệp và thư mục. Tôi đã loại bỏ biểu thức grep và đã awk thực hiện tất cả các bộ lọc : awk '/: gone]/{if ($1!="*") print $1}'. Điều này bây giờ hoạt động như mong đợi.
rhaben

5
@ahmedbhs kể từ khi lệnh sử dụng dấu nháy đơn 'bạn cần phải sử dụng dấu nháy kép "bao vây toàn bộ lệnh Ngoài ra, bí danh git đó là các lệnh shell (như thế này) yêu cầu ngay từ đầu này hoạt động đối với tôi:.!.test = "!git fetch -p && for branch in `git branch -vv | grep ': gone]' | awk '{print $1}'`; do git branch -D $branch; done"
jason.rickman

306

Hầu hết các câu trả lời không thực sự trả lời câu hỏi ban đầu. Tôi đã thực hiện một loạt các hoạt động đào và đây là giải pháp sạch nhất tôi tìm thấy. Đây là một phiên bản kỹ lưỡng hơn của câu trả lời:

  1. Kiểm tra chi nhánh mặc định của bạn. Thông thườnggit checkout master
  2. Chạy git fetch -p && git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -d

Giải trình:

Hoạt động bằng cách cắt tỉa các nhánh theo dõi của bạn sau đó xóa các nhánh cục bộ cho thấy chúng đã "biến mất" git branch -vv.

Ghi chú:

Nếu ngôn ngữ của bạn được đặt thành một từ khác ngoài tiếng Anh, bạn sẽ cần phải đổi gonethành từ thích hợp. Các chi nhánh là địa phương sẽ không được chạm vào. Các nhánh đã bị xóa trên remote nhưng không được hợp nhất sẽ hiển thị thông báo nhưng không bị xóa trên local. Nếu bạn muốn xóa những cái đó cũng thay đổi -dthành -D.


2
đối với hệ điều hành tiếng Pháp đã phải thay đổi bằng cách chênh lệch
Mohamed EL HABIB

4
nên thêm LANG = en_US trước chi nhánh git để buộc tiếng Anh: git fetch --prune && LANG = en_US git chi nhánh -vv | awk '/: đã biến mất] / {in $ 1}' | xargs git chi nhánh -d
Mohamed EL HABIB

3
Tôi sẽ thêm git checkout master && ...vào đầu lệnh.
iarroyo

2
Điều này rất nguy hiểm khi bạn ở trên một nhánh được cho là bị xóa - trong trường hợp đó, cột đầu tiên là '*', sau đó được chuyển đến xargs. Để cải thiện điều này, hãy thêm dải ký tự '*' trước khi chuyển đầu ra cho awk: sed -e 's / ^ * //'
meeee

6
Cẩn thận! Có một trường hợp cạnh sẽ dẫn đến xóa tất cả các nhánh cục bộ của bạn: Nếu bạn hiện đang ở trên một nhánh đã bị xóa trên điều khiển từ xa, đầu ra git branch -vvsẽ bắt đầu bằng dấu hoa thị, kết quả cuối cùng sẽ thực hiện git branch -d *. Đây là một phiên bản vá sẽ bỏ qua các dòng có dấu hoa thị:git branch -vv | grep ': gone]'| grep -v "\*" | awk '{ print $1; }' | xargs -r git branch -d
kcm

210

Tôi thường không trả lời một câu hỏi đã có 16 câu trả lời, nhưng tất cả các câu trả lời khác đều sai và câu trả lời đúng rất đơn giản. Câu hỏi cho biết: "Có cách nào đơn giản để xóa tất cả các nhánh theo dõi mà tương đương từ xa không còn tồn tại không?"

Nếu "đơn giản" có nghĩa là xóa tất cả chúng trong một lần, không mong manh, không nguy hiểm và không phụ thuộc vào các công cụ mà không phải tất cả độc giả sẽ có, thì câu trả lời đúng là: không.

Một số câu trả lời rất đơn giản, nhưng họ không làm những gì được hỏi. Những người khác làm những gì được yêu cầu, nhưng không đơn giản: tất cả đều dựa vào phân tích đầu ra Git thông qua các lệnh thao tác văn bản hoặc ngôn ngữ kịch bản, có thể không có trên mọi hệ thống. Trên hết, hầu hết các đề xuất đều sử dụng các lệnh bằng sứ, mà đầu ra của nó không được thiết kế để được phân tích cú pháp bởi tập lệnh ("sứ" dùng để chỉ các lệnh dành cho hoạt động của con người; các tập lệnh nên sử dụng các lệnh "ống nước" cấp thấp hơn).

Đọc thêm:


Nếu bạn muốn thực hiện việc này một cách an toàn, đối với trường hợp sử dụng trong câu hỏi (các nhánh theo dõi thu gom rác đã bị xóa trên máy chủ nhưng vẫn tồn tại dưới dạng các nhánh cục bộ) và chỉ với các lệnh Git cấp cao, bạn phải

  • git fetch --prune(hoặc git fetch -p, đó là một bí danh, hoặc git prune remote originthực hiện điều tương tự mà không cần tìm nạp, và có lẽ không phải là điều bạn muốn hầu hết thời gian).
  • Lưu ý bất kỳ chi nhánh từ xa được báo cáo là đã xóa. Hoặc, để tìm thấy chúng sau này, git branch -v(bất kỳ nhánh theo dõi mồ côi nào sẽ được đánh dấu "[biến mất]").
  • git branch -d [branch_name] trên mỗi nhánh theo dõi mồ côi

(đó là những gì một số câu trả lời khác đề xuất).

Nếu bạn muốn viết kịch bản một giải pháp, thì đó for-each-reflà điểm khởi đầu của bạn, như trong câu trả lời của Mark Longair ở đây và câu trả lời này cho một câu hỏi khác , nhưng tôi không thể thấy cách khai thác mà không cần viết vòng lặp shell shell, hoặc sử dụng xargs hoặc một cái gì đó .


Giải thích lý lịch

Để hiểu những gì đang xảy ra, bạn cần đánh giá cao rằng, trong tình huống theo dõi các chi nhánh, bạn không chỉ có một chi nhánh, mà là ba. (Và nhớ lại rằng "nhánh" có nghĩa đơn giản là một con trỏ tới một cam kết.)

Đưa ra một nhánh theo dõi feature/X, kho lưu trữ từ xa (máy chủ) sẽ có nhánh này và gọi nó feature/X. Kho lưu trữ cục bộ của bạn có một nhánh remotes/origin/feature/Xcó nghĩa là "Đây là những gì mà điều khiển từ xa nói với tôi về tính năng / nhánh X của nó, lần trước chúng ta đã nói chuyện" và cuối cùng, kho lưu trữ cục bộ có một nhánh feature/Xtrỏ đến cam kết mới nhất của bạn và được định cấu hình để "Theo dõi" remotes/origin/feature/X, có nghĩa là bạn có thể kéo và đẩy để giữ cho chúng thẳng hàng.

Tại một số điểm, ai đó đã xóa feature/Xtrên điều khiển từ xa. Từ lúc đó, bạn bị bỏ lại với địa phương của mình feature/X(điều mà bạn có thể không muốn nữa, vì công việc trên tính năng X có lẽ đã kết thúc) và remotes/origin/feature/Xđiều đó chắc chắn là vô dụng vì mục đích duy nhất của nó là ghi nhớ trạng thái của chi nhánh máy chủ .

Và Git sẽ cho phép bạn tự động dọn sạch phần dư thừa remotes/origin/feature/X- đó là những gì git fetch --prune- nhưng vì một số lý do, nó không cho phép bạn tự động xóa feature/X... mặc dù bạn feature/Xvẫn chứa thông tin theo dõi mồ côi, vì vậy nó có thông tin để xác định các nhánh theo dõi cũ đã được sáp nhập hoàn toàn. (Rốt cuộc, nó có thể cung cấp cho bạn thông tin cho phép bạn tự thực hiện thao tác.)


3
Đây là một câu trả lời an toàn hơn nhiều. Ngoài ra, nó sẽ hoạt động nếu bạn sử dụng quy trình "Squash and Merge", không giống như câu trả lời đã chọn.
Jake Levitt

3
Điều này thực sự chỉ trả lời phần dễ dàng của câu hỏi "làm thế nào để tìm các nhánh bị mất" (và với lệnh tương tự như đã được đăng bởi jason.rickman vào năm 2015) nhưng sau đó cho bạn biết xóa tất cả các nhánh theo cách thủ công chính xác là OP không muốn làm.
Voo

4
@Voo Đó là điểm của câu trả lời: không cho bạn biết cách thực hiện (đó chỉ là phần thưởng) mà là cho bạn biết câu trả lời là không có cách nào dễ dàng, đơn giản để làm điều đó. Đó là câu trả lời chính xác cho câu hỏi như nó đã được xây dựng.
Andrew Spencer

1
"Nếu bạn muốn làm điều này một cách an toàn, cho trường hợp sử dụng trong câu hỏi .." - điều này phủ nhận hầu hết các khiếu nại của đoạn đầu tiên. "[Git] có thể cung cấp cho bạn thông tin cho phép bạn tự thực hiện thao tác" - chúng tôi là lập trình viên, do đó, cung cấp thông tin dưới dạng danh sách (được hiển thị), nhiệm vụ vẫn đơn giản (ví dụ xargs:). Ngoài ra còn git aliasđể đơn giản hóa một số trường hợp.
dùng2864740

2
@ user2864740 Một số độc giả có thể cho rằng việc viết một tập lệnh bash nhỏ nằm trong định nghĩa "đơn giản" của họ và đó là một vị trí hoàn toàn có thể phòng thủ được, nhưng tôi đã đưa ra cách giải thích của riêng mình về "đơn giản" trong đoạn 2 và phần còn lại của câu trả lời là nhất quán với nó. Và để bảo vệ sự giải thích hạn chế được thừa nhận của tôi, không phải tất cả người dùng Git đều có xargshoặc bash, và có lẽ họ không ở đây để tìm kiếm một thách thức về mã hóa vi mô, nhiều như một câu trả lời nhanh chóng mà họ có thể áp dụng một cách an toàn mà không làm sao lãng khỏi mục tiêu thực sự của họ .
Andrew Spencer

52

Tôi tìm thấy câu trả lời ở đây: Làm thế nào tôi có thể xóa tất cả các nhánh git đã được hợp nhất?

git branch --merged | grep -v "\*" | xargs -n 1 git branch -d

Hãy chắc chắn rằng chúng tôi giữ chủ

Bạn có thể đảm bảo rằng master, hoặc bất kỳ chi nhánh nào khác cho vấn đề đó, không bị xóa bằng cách thêm chi nhánh khác grepsau lần đầu tiên. Trong trường hợp đó bạn sẽ đi:

git branch --merged | grep -v "\*" | grep -v "YOUR_BRANCH_TO_KEEP" | xargs -n 1 git branch -d

Vì vậy, nếu chúng tôi muốn giữ master, developstagingví dụ, chúng tôi sẽ đi:

git branch --merged | grep -v "\*" | grep -v "master" | grep -v "develop" | grep -v "staging" | xargs -n 1 git branch -d

Làm cho điều này một bí danh

Vì nó hơi dài, bạn có thể muốn thêm bí danh cho .zshrchoặc .bashrc. Của tôi được gọi là gbpurge(cho git branches purge):

alias gbpurge='git branch --merged | grep -v "\*" | grep -v "master" | grep -v "develop" | grep -v "staging" | xargs -n 1 git branch -d'

Sau đó tải lại .bashrchoặc .zshrc:

. ~/.bashrc

hoặc là

. ~/.zshrc

8
Hữu ích, nhưng không giống như "chi nhánh loại bỏ không trên xa"
dlsso

Greate trả lời @karlingen, tôi có điều này vĩnh viễn như gbpurge trên tất cả các môi trường dev của tôi
Peter Dolan

50

Giải pháp Windows

Đối với Microsoft Windows Powershell:

git checkout master; git remote update origin --prune; git branch -vv | Select-String -Pattern ": gone]" | % { $_.toString().Trim().Split(" ")[0]} | % {git branch -d $_}

Giải thích

git checkout master chuyển sang nhánh chính

git remote update origin --prune cắt tỉa cành từ xa

git branch -vvnhận được một đầu ra dài dòng của tất cả các chi nhánh ( tham chiếu git )

Select-String -Pattern ": gone]" chỉ nhận được các bản ghi nơi chúng đã bị xóa từ xa.

% { $_.toString().Trim().Split(" ")[0]} lấy tên chi nhánh

% {git branch -d $_} xóa chi nhánh


3
Cảm ơn vì điều này, mặc dù tôi đã phải thêm một .Trim()sau .toString()để xóa hai khoảng trắng trước tên chi nhánh.
Matthew

2
Cảm ơn lệnh này. Tôi đã phải thay đổi git branch -dthành git branch -Dkhác tôi nhận được lỗi chi nhánh không được hợp nhất hoàn toàn.
markieo

1
@markieo Có thể bạn đã biết điều này rồi, nhưng đối với bất kỳ ai khác - git branch -Dphá hoại và sẽ xóa công việc địa phương mà bạn chưa đẩy. -dluôn luôn an toàn, chỉ cần cẩn thận với -D.
Nate Barbettini

33

Mẫu phù hợp cho "biến mất" trong hầu hết các giải pháp khác là một chút đáng sợ đối với tôi. Để an toàn hơn, điều này sử dụng --formatcờ để kéo trạng thái theo dõi ngược dòng của mỗi nhánh .

Tôi cần một phiên bản thân thiện với Windows, vì vậy, điều này sẽ xóa tất cả các nhánh được liệt kê là "biến mất" bằng cách sử dụng Powershell:

git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" | 
    ? { $_ -ne "" } | 
    % { git branch -D $_ }

Dòng đầu tiên liệt kê tên của các nhánh địa phương có nhánh ngược dòng là "biến mất". Dòng tiếp theo sẽ loại bỏ các dòng trống (là đầu ra cho các nhánh không "biến mất"), sau đó tên nhánh được chuyển đến lệnh để xóa nhánh.


6
Các --formattùy chọn có vẻ là khá mới; Tôi cần nâng cấp git từ 2.10.s Something lên 2.16.3 để có được nó. Đây là sửa đổi của tôi cho các hệ thống Linux-ish:git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)" | sed 's,^refs/heads/,,' | grep . | xargs git branch -D
bxm

2
Đây là giải pháp tốt nhất cho đến nay. Một gợi ý là sử dụng refname:short. Sau đó, bạn có thể xóa dòng% { $_ -replace '^refs/heads/', '' }
ioudas

2
Đây là một giải pháp tuyệt vời cho các cửa sổ. Tôi đang sử dụng nó như thế này vì nó dễ đọc hơn git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" | where { $_ -ne "" } | foreach { git branch -d $_ }Cũng có thể là một ý tưởng tốt để sử dụng -dthay vì -D. Buộc xóa không cần thiết cho các nhánh không còn trên remote.
Rubanov

31

Xóa tất cả các nhánh đã được hợp nhất thành chủ, nhưng đừng cố xóa chính nó:

git checkout master && git pull origin master && git fetch -p && git branch -d $(git branch --merged | grep master -v)

hoặc thêm bí danh:

alias gitcleanlocal="git checkout master && git pull origin master && git fetch -p && git branch -d $(git branch --merged | grep master -v)"

Giải trình:

git checkout master kiểm tra chi nhánh tổng thể

git pull origin master đảm bảo chi nhánh địa phương có tất cả các thay đổi từ xa được hợp nhất

git fetch -p xóa tham chiếu đến các nhánh từ xa đã bị xóa

git branch -d $(git branch master --merged | grep master -v) xóa tất cả các nhánh đã được hợp nhất thành chủ, nhưng đừng cố xóa chính nó


3
Một lưu ý, điều này rất hữu ích nhưng nó cũng sẽ loại bỏ những nhánh chưa bao giờ được đẩy ra điều khiển từ xa. An toàn hơn khi chỉ liệt kê các khác biệt và sau đó sao chép những gì bạn thực sự muốn xóa vào git branch -Dlệnh
Zefiryn

Chỉ cần làm rõ, Zefiryn đang đề cập đến việc sử dụng tùy chọn -D không phải là một phần của lớp lót.
cs01

1
Hoặc sử dụng chữ thường git branch -dsẽ đưa ra cảnh báo về việc không đẩy các nhánh.
acme

16
git fetch -p

Điều này sẽ cắt tỉa bất kỳ chi nhánh không còn tồn tại trên điều khiển từ xa.


64
điều này loại bỏ các tham chiếu từ xa, nhưng không phải các chi nhánh địa phương. Trong khi một lệnh hữu ích, tôi không nghĩ câu trả lời này của OP.
thataustin

Gì? Điều này xóa các chi nhánh địa phương cho tôi.
Alex Hall

1
@AlexHall Kho lưu trữ từ xa có một chi nhánh X; bạn git checkout X; bây giờ kho lưu trữ của bạn có một nhánh theo dõi (cục bộ) Xvà một nhánh từ xa origin/X; kho lưu trữ từ xa xóa X; bạn git fetch-p; trong kho lưu trữ cục bộ của bạn, không chỉ origin/Xmà còn Xbị xóa. Có phải đó là những gì bạn đang nói?
Andrew Spencer

16

Tuy nhiên, một câu trả lời khác cho đống, rút ​​ra rất nhiều từ câu trả lời của Patrick (điều mà tôi thích bởi vì nó dường như làm mất đi bất kỳ sự mơ hồ nào về nơi gone]sẽ khớp với git branchđầu ra) nhưng thêm một * nix uốn cong.

Ở dạng đơn giản nhất:

git branch --list --format \
  "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" \
  | xargs git branch -D

Tôi có điều này gói gọn trong một git-gonekịch bản trên con đường của tôi:

#!/usr/bin/env bash

action() {
  ${DELETE} && xargs git branch -D || cat
}

get_gone() {
  git branch --list --format \
    "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)"
}

main() {
  DELETE=false
  while [ $# -gt 0 ] ; do
    case "${1}" in
      (-[dD] | --delete) DELETE=true ;;
    esac
    shift
  done
  get_gone | action
}

main "${@}"

NB - --formatTùy chọn có vẻ khá mới; Tôi cần nâng cấp git từ 2.10.s Something lên 2.16.3 để có được nó.

EDIT: được điều chỉnh để bao gồm gợi ý về refname:shorttừ Benjamin W.

NB2 - Tôi mới chỉ thử nghiệm bash, do đó là hashbang, nhưng có lẽ là di động sh.


Bạn không thể bỏ qua sedphần bằng cách sử dụng %(refname:short)trong dòng đầu tiên?
Benjamin W.

Có vẻ như để làm việc cho tôi, và bây giờ tôi có nó như một bí danh Git gọn gàng - giải pháp sạch nhất trong số chúng ở đây!
Benjamin W.

2,13 giới thiệu --format. Một cách khác để bỏ qua sedphần là sử dụng git update-ref -d. Lưu ý rằng điều này có lẽ không an toàn, sử dụng git for-each-refở đây an toàn hơn (được đưa ra --shell).
gsnedder

Câu trả lời tuyệt vời, và nó đã giúp tôi trả lời các câu hỏi của chính mình khi loay hoay --formattự mình làm điều này. Chỉ một câu hỏi: tại sao #!bash? Tất cả mọi thứ ở đây trông giống như di động shvới tôi.
Toby Speight

Đó chỉ là cái nồi hơi bình thường của tôi, và đã không thử nghiệm ở nơi khác.
bxm

15

Có thể hữu ích với một số người, một dòng đơn giản để xóa tất cả các nhánh cục bộ trừ chủ và phát triển

git branch | grep -v "master" | grep -v "develop" | xargs git branch -D

Câu trả lời tuyệt vời! Tôi thích cách người ta có thể dễ dàng chơi với các 'git branch | grep -v "master" | grep -v "develop"loại công cụ trước khi cam kết thêm vào phần xóa của lệnh. 👏😊
finneycanhelp

Nhưng điều này không trả lời câu hỏi trên. Điều này sẽ xóa các chi nhánh mặc dù điều khiển từ xa vẫn còn đó.
Jim.B

13

Điều này sẽ xóa tất cả các nhánh cục bộ được hợp nhất ngoại trừ tham chiếu chính cục bộ và tham chiếu hiện đang được sử dụng:

git branch --merged | grep -v "*" | grep -v "master" | xargs git branch -d

Và điều này sẽ xóa tất cả các nhánh đã bị xóa khỏi kho lưu trữ từ xa được tham chiếu bởi " origin ", nhưng vẫn có sẵn cục bộ trong " điều khiển từ xa / nguồn gốc ".

git remote prune origin

git branch -vv | grep 'gone]' | grep -v "\*" | awk '{print $1}' | xargs -r git branch -d Giải thích: Tôi thích thay thế git branch --mergedbằng cách git branch -vvhiển thị trạng thái (đã biến mất) vì trước đó git branch --mergedcũng có thể hiển thị chính
jpmottin

13

Tôi không nghĩ rằng có một lệnh tích hợp để làm điều này, nhưng nó an toàn để làm như sau:

git checkout master
git branch -d bug-fix-a

Khi bạn sử dụng -d, git sẽ từ chối xóa chi nhánh trừ khi nó được hợp nhất hoàn toàn vào HEADhoặc chi nhánh theo dõi từ xa ngược dòng của nó. Vì vậy, bạn luôn có thể lặp qua đầu ra git for-each-refvà cố gắng xóa từng nhánh. Vấn đề với cách tiếp cận đó là tôi nghi ngờ rằng có lẽ bạn không muốn bug-fix-dbị xóa chỉ vì origin/bug-fix-dchứa lịch sử của nó. Thay vào đó, bạn có thể tạo một tập lệnh giống như sau:

#!/bin/sh

git checkout master &&
for r in $(git for-each-ref refs/heads --format='%(refname:short)')
do
  if [ x$(git merge-base master "$r") = x$(git rev-parse --verify "$r") ]
  then
    if [ "$r" != "master" ]
    then
      git branch -d "$r"
    fi
  fi
done

Cảnh báo: Tôi chưa kiểm tra tập lệnh này - chỉ sử dụng cẩn thận ...


Tôi lấy tự do để chỉnh sửa kịch bản. Vẫn không đưa ra bất kỳ đảm bảo nào, nhưng, nó chạy và dường như hoạt động ngay bây giờ.
fwielstra

13

TL; DR:

Xóa TẤT CẢ các chi nhánh địa phương không ở xa

git fetch -p && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -D

Xóa TẤT CẢ các chi nhánh địa phương không ở xa VÀ được hợp nhất hoàn toàn VÀ không được sử dụng như đã nói trong nhiều câu trả lời trước đây.

git fetch -p && git branch --merged | grep -v '*' | grep -v 'master' | xargs git branch -d

Giải trình

  • git fetch -p sẽ cắt tỉa tất cả các chi nhánh không còn tồn tại trên điều khiển từ xa
  • git branch -vv sẽ in các nhánh cục bộ và nhánh được cắt tỉa sẽ được gắn thẻ gone
  • grep ': gone]' chỉ chọn chi nhánh đã biến mất
  • awk '{print $1}' lọc đầu ra để chỉ hiển thị tên của các nhánh
  • xargs git branch -D sẽ lặp trên tất cả các dòng (nhánh) và buộc loại bỏ nhánh này

Tại sao git branch -Dvà không phải là git branch -dbạn sẽ có cho các chi nhánh không được hợp nhất hoàn toàn.

error: The branch 'xxx' is not fully merged.

1
bạn có muốn nói từ xa thay vì chủ?
tinos

8

Bạn có thể làm điều này:

git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d

7

Dựa trên thông tin ở trên, điều này làm việc cho tôi:

git br -d `git br -vv | grep ': gone] ' | awk '{print $1}' | xargs`

Nó loại bỏ tất cả các chi nhánh địa phương với ': gone] 'trên điều khiển từ xa.


1
Dường như điều này cũng sẽ loại bỏ bất kỳ chi nhánh nào đã "biến mất" trong thông điệp cam kết cuối cùng.
dlsso

Nó cũng sẽ loại bỏ bất kỳ chi nhánh có gonebất cứ nơi nào trong tên của nó.
bfontaine

3
"git chi nhánh -D git chi nhánh -vv | grep ': gone]' | awk '{print $ 1}' | xargs`" Điều này đã làm việc cho tôi.
Muthu Ganapathy Nathan

7
grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -d

Lệnh trên có thể được sử dụng để tìm nạp các nhánh được hợp nhất và xóa trong điều khiển từ xa và nó sẽ xóa các nhánh cục bộ không còn khả dụng trong điều khiển từ xa


Giải pháp tốt nhất cho đến nay, mặc dù tôi đã thay đổi nó grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -Dđể buộc xóa tất cả
Shoaib

Nguy hiểm nếu bất kỳ tên chi nhánh nào của bạn xảy ra có chứa chuỗi con goneở bất kỳ đâu trong chúng (ví dụ usingonefunction).
Toby Speight

7

Không ai trong số này thực sự phù hợp với tôi. Tôi muốn một cái gì đó sẽ thanh lọc tất cả các chi nhánh địa phương đang theo dõi một chi nhánh từ xa, trên đó origin, nơi chi nhánh từ xa đã bị xóa ( gone). Tôi không muốn xóa các nhánh cục bộ không bao giờ được thiết lập để theo dõi một nhánh từ xa (ví dụ: các nhánh dev cục bộ của tôi). Ngoài ra tôi muốn có một lớp lót đơn giản chỉ sử dụng githoặc các công cụ CLI đơn giản khác, thay vì viết các tập lệnh tùy chỉnh. Tôi đã kết thúc bằng cách sử dụng một chút grepawk để thực hiện lệnh đơn giản này.

Đây cuối cùng là những gì kết thúc trong tôi ~/.gitconfig:

[alias]
  prune-branches = !git remote prune origin && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -D

Đây là một git config --global ...lệnh để dễ dàng thêm điều này như git prune-branches:

git config --global alias.prune-branches '!git remote prune origin && git branch -vv | grep '"'"': gone]'"'"' | awk '"'"'{print $1}'"'"' | xargs -r git branch -d'

LƯU Ý: Trong lệnh config, tôi sử dụng -dtùy chọn git branchthay vì -D, như tôi làm trong cấu hình thực tế của mình. Tôi sử dụng -Dvì tôi không muốn nghe Git phàn nàn về các chi nhánh không được trộn lẫn. Bạn có thể muốn chức năng này là tốt. Nếu vậy, chỉ cần sử dụng -Dthay vì -dở cuối lệnh config.


Tôi thích cách tiếp cận bí danh git. Câu hỏi nhanh: Tại sao làm git branch -vvvà gre cho : gone]và không làm git branch -vvà grep cho [gone]?
lalibi

@lalibi, câu hỏi hay. Tôi không nhớ. Tôi nghi ngờ rằng một cái gì đó không hiển thị chỉ với mộtv . Nếu git branch -v | grep '[gone]'làm việc cho bạn, đi cho nó. Nó có vẻ sạch sẽ hơn một chút.
Karl Wilbur

4

Dựa trên Git Tip: Xóa các nhánh cục bộ cũ , trông giống như giải pháp của jason.rickman, tôi đã thực hiện một lệnh tùy chỉnh cho mục đích này được gọi là git đi bằng Bash:

$ git gone
usage: git gone [-pndD] [<branch>=origin]
OPTIONS
  -p  prune remote branch
  -n  dry run: list the gone branches
  -d  delete the gone branches
  -D  delete the gone branches forcefully

EXAMPLES
git gone -pn    prune and dry run
git gone -d     delete the gone branches

git gone -pn kết hợp việc cắt tỉa và liệt kê các nhánh "đã biến mất":

$ git gone -pn
  bport/fix-server-broadcast         b472d5d2b [origin/bport/fix-server-broadcast: gone] Bump modules
  fport/rangepos                     45c857d15 [origin/fport/rangepos: gone] Bump modules

Sau đó, bạn có thể bóp cò bằng git gone -dhoặcgit gone -D .

Ghi chú

  • Biểu thức chính quy tôi đã sử dụng là "$BRANCH/.*: gone]"nơi $BRANCHthường sẽ làorigin . Điều này có thể sẽ không hoạt động nếu đầu ra Git của bạn được bản địa hóa sang tiếng Pháp, v.v.
  • Sebastian Wiesner cũng đã chuyển nó cho Rust cho người dùng Windows. Cái đó cũng được gọi là git đi .

Đây có lẽ nên là câu trả lời được chấp nhận - git gonetrông giống như một bí danh git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d, giải quyết vấn đề của OP.
alex

4

Vẽ nặng nề từ một số các khác câu trả lời ở đây, tôi đã kết thúc với những điều sau đây (git 2.13 trở lên mà thôi, tôi tin), mà nên làm việc trên bất kỳ tựa UNIX shell:

git for-each-ref --shell --format='ref=%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)' refs/heads | while read entry; do eval "$entry"; [ ! -z "$ref" ] && git update-ref -d "$ref" && echo "deleted $ref"; done

Điều này đáng chú ý sử dụng for-each-refthay vì branch(như branchmột lệnh "sứ" được thiết kế cho đầu ra có thể đọc được của con người, không phải xử lý máy) và sử dụng --shellđối số của nó để có đầu ra thoát đúng (điều này cho phép chúng tôi không lo lắng về bất kỳ ký tự nào trong tên ref).


Nó hoạt động với tôi, nhưng cũng tốt nếu bạn có thể giải thích các bước khác trong lệnh đang làm gì. Ví dụ tôi không biết [ ! -z "$ref" ]nghĩa là gì . Tôi nghĩ rằng một lót nhiều sẽ có ích. Nhưng vẫn cảm ơn cho đầu vào của bạn!
KevinH

4

Một câu trả lời khác, bởi vì không có giải pháp nào phù hợp với nhu cầu của tôi về sự thanh lịch và đa nền tảng:

Lệnh xóa các nhánh cục bộ không phải từ xa:

for b in $(git for-each-ref --format='%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' refs/heads); do git branch -d $b; done

Để tích hợp nó với gitconfig để có thể chạy với git branch-prune :

Bash

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format='\''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'\'' refs/heads); do git branch -d $b; done'

PowerShell

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format=''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'' refs/heads); do git branch -d $b; done'

(Cần trợ giúp trong việc tìm kiếm một lệnh phổ quát cho PowerShell và bash)

Tại sao câu trả lời này là tốt nhất?

  • Cung cấp một giải pháp hoàn chỉnh: thêm một git branch-prune lệnh vào git của bạn
  • Hoạt động tốt từ Windows PowerShell
  • Ý tưởng cốt lõi là phương pháp chống đạn của @ jason.rickman bằng cách sử dụnggit for-each-ref
  • Phân tích cú pháp và lọc được thực hiện --filtermà không cần phụ thuộc bên ngoài

Giải trình:

  • Thêm một bí danh mới cho bạn ~\.gitconfig . Sau khi thực hiện điều này, bạn chỉ cần làmgit branch-prune
  • Bên trong bí danh này:
    • Lấy các nhánh với --prune cờ, "cắt tỉa các nhánh theo dõi từ xa không còn trên điều khiển từ xa"
    • Sử dụng git for-each-ref--filter, để có được một danh sách các nhánh là [gone](không có từ xa)
    • Vòng lặp thông qua danh sách này và xóa chi nhánh một cách an toàn

1
Cảm ơn bạn, @ jerry-wu vì đã cải thiện sự tuyệt vời của giải pháp này đến vô cùng.
Himura

@ jerry-wu và lần chỉnh sửa lệnh git config cuối cùng của bạn không hoạt động trong PowerShell ((
Himura

1

Tôi đã đưa ra kịch bản bash này. Nó luôn luôn giữ cho chi nhánh develop, qa, master.

git-clear() {
  git pull -a > /dev/null

  local branches=$(git branch --merged | grep -v 'develop' | grep -v 'master' | grep -v 'qa' | sed 's/^\s*//')
  branches=(${branches//;/ })

  if [ -z $branches ]; then
    echo 'No branches to delete...'
    return;
  fi

  echo $branches

  echo 'Do you want to delete these merged branches? (y/n)'
  read yn
  case $yn in
      [^Yy]* ) return;;
  esac

  echo 'Deleting...'

  git remote prune origin
  echo $branches | xargs git branch -d
  git branch -vv
}

1

Tôi sử dụng một phương pháp ngắn để thực hiện thủ thuật, tôi khuyên bạn nên làm tương tự vì nó có thể tiết kiệm một số giờ và cung cấp cho bạn khả năng hiển thị nhiều hơn

Chỉ cần thêm đoạn mã sau vào .bashrc (.bashprofile trên macos).

git-cleaner() { git fetch --all --prune && git branch --merged | grep -v -E "\bmaster|preprod|dmz\b" | xargs -n 1 git branch -d ;};
  1. Lấy tất cả các điều khiển từ xa
  2. Chỉ nhận các chi nhánh hợp nhất từ ​​git
  3. Xóa khỏi danh sách này các nhánh "được bảo vệ / quan trọng"
  4. Loại bỏ phần còn lại (ví dụ: làm sạch và sáp nhập các nhánh)

Bạn sẽ phải chỉnh sửa regex grep để phù hợp với nhu cầu của bạn (ở đây, nó ngăn chặn master, preprod và dmz khỏi bị xóa)


git fetch --all --pruneđã lừa Cảm ơn!
LeOn - Han Li

1

Điều này làm việc cho tôi:

git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d

oh, tôi đã đăng đôi bài này lên ... chỉ thấy điều này khi xem bài viết. Nhưng bạn thực sự có thể chỉ vào anh chàng đã cung cấp lệnh này thay vì nhận nó như của bạn!
Dwza

Tôi nhận được nó từ một diễn đàn đừng cố tỏ ra thông minh chỉ cần trả lời hoặc bỏ qua nó
Fareed Alnamrouti

Thậm chí nhiều hơn bạn có thể ... dù sao ... đã không đến đây để thảo luận điều này với bạn. Như bạn đã nói .. hãy nhận xét của tôi hoặc bỏ qua nó;)
Dwza

0

Tôi không chắc chắn trong bao lâu, nhưng tôi sử dụng git-up bây giờ, nó sẽ chăm sóc điều đó.

Tôi làm git upvà nó bắt đầu theo dõi các nhánh mới và xóa các nhánh cũ.

Chỉ cần làm cho nó rõ ràng, đó không phải là lệnh git ngoài luồng - https://github.com/aanand/git-up

BTW nó cũng đâm cây bẩn và làm cho cuộc nổi loạn vẫn còn với chỉ git up.

Hy vọng nó sẽ hữu ích cho ai đó


2
Điều này không xóa các chi nhánh địa phương không tồn tại trên máy chủ.
Ashley

0

Đây là một giải pháp mà tôi sử dụng cho vỏ cá. Đã thử nghiệm trên Mac OS X 10.11.5, fish 2.3.0git 2.8.3.

function git_clean_branches
  set base_branch develop

  # work from our base branch
  git checkout $base_branch

  # remove local tracking branches where the remote branch is gone
  git fetch -p

  # find all local branches that have been merged into the base branch
  # and delete any without a corresponding remote branch
  set local
  for f in (git branch --merged $base_branch | grep -v "\(master\|$base_branch\|\*\)" | awk '/\s*\w*\s*/ {print $1}')
    set local $local $f
  end

  set remote
  for f in (git branch -r | xargs basename)
    set remote $remote $f
  end

  for f in $local
    echo $remote | grep --quiet "\s$f\s"
    if [ $status -gt 0 ]
      git branch -d $f
    end
  end
end

Một vài lưu ý:

Hãy chắc chắn để đặt chính xác base_branch. Trong trường hợp này tôi sử dụng developlàm nhánh cơ sở, nhưng nó có thể là bất cứ thứ gì.

Phần này rất quan trọng : grep -v "\(master\|$base_branch\|\*\)". Nó đảm bảo rằng bạn không xóa chủ hoặc chi nhánh cơ sở của bạn.

Tôi sử dụng git branch -d <branch>như một biện pháp phòng ngừa bổ sung, để không xóa bất kỳ chi nhánh nào chưa được hợp nhất hoàn toàn với đầu nguồn hoặc hiện tại.

Một cách dễ dàng để kiểm tra là thay thế git branch -d $fbằng echo "will delete $f".

Tôi cho rằng tôi cũng nên thêm: SỬ DỤNG RỦI RO RIÊNG CỦA BẠN!


0

Tôi đã viết một tập lệnh Python bằng GitPython để xóa các nhánh cục bộ không tồn tại trên remote.

    import git
    import subprocess
    from git.exc import GitCommandError
    import os

    def delete_merged_branches():
        current_dir = input("Enter repository directory:")
        repo = git.Repo(current_dir)
        git_command = git.Git(current_dir)

        # fetch the remote with prune, this will delete the remote references in local.
        for remote in repo.remotes:
            remote.fetch(prune=True)

        local_branches = [branch.name for branch in repo.branches]
        deleted_branches = []

        # deleted_branches are the branches which are deleted on remote but exists on local.
        for branch in local_branches:
            try:
                remote_name = 'origin/'+ branch
                repo.git.checkout(remote_name)
            except GitCommandError:
            # if the remote reference is not present, it means the branch is deleted on remote.
                deleted_branches.append(branch)

        for branch in deleted_branches:
            print("Deleting branch:"+branch)
            git_command.execute(["git", "branch", "-D",branch])


        # clean up the work flow.
        repo.git.checkout('master')
        repo.git.pull()

    if __name__ == '__main__':
        delete_merged_branches()

Hy vọng ai đó thấy nó hữu ích, xin vui lòng thêm ý kiến ​​nếu tôi bỏ lỡ bất cứ điều gì.


0

Nếu bạn đang sử dụng zshshell Oh My Zshđã cài đặt thì cách dễ nhất để thực hiện việc này một cách an toàn là sử dụng tính năng tự động hoàn thành tích hợp.

Trước tiên, xác định các nhánh bạn muốn xóa với:

~ git branch --merged

  branch1
  branch2
  branch3
* master

Điều này sẽ cho bạn thấy một danh sách các chi nhánh đã được sáp nhập

Sau khi bạn biết một vài bạn muốn xóa, hãy gõ:

~ git branch -d 

Tất cả bạn phải làm là nhấn [tab] và nó sẽ hiển thị cho bạn một danh sách các chi nhánh địa phương. Sử dụng hoàn thành tab hoặc chỉ cần nhấn lại [tab] và bạn có thể quay vòng qua chúng để chọn một nhánh có [enter].

Tab Chọn các nhánh nhiều lần cho đến khi bạn có một danh sách các nhánh bạn muốn xóa:

~ git branch -d branch1 branch2 branch3

Bây giờ chỉ cần nhấn enter để xóa bộ sưu tập các chi nhánh của bạn.

Nếu bạn không sử dụng zsh trên thiết bị đầu cuối của mình ... Hãy lấy nó ở đây.


0

Tôi thích sử dụng ống vì nó làm cho lệnh dễ đọc hơn.

Đây là giải pháp của tôi nếu bạn muốn loại bỏ tất cả các chi nhánh trừ chủ.

git branch | grep -v master | xargs -n 1 git branch -D

Để xóa các nhánh khác phù hợp với tiêu chí của bạn, hãy sửa đổi khối thứ nhất và thứ hai.

git branch --merged | grep feature_name | xargs -n 1 git branch -D

-3

Đây là câu trả lời đơn giản phù hợp với tôi khi sử dụng ứng dụng khách git:

Xóa kho lưu trữ hoàn toàn khỏi máy của bạn sau đó kiểm tra lại.

Không có mucking xung quanh với các kịch bản rủi ro.


2
Còn các chi nhánh tôi hiện đang làm việc: /
Mailo Světel

@ MailoSvětel nếu chúng ở trên điều khiển từ xa của bạn thì bạn chỉ cần kéo chúng lại (nhớ cam kết và đẩy công việc mới nhất của bạn đến điều khiển từ xa!)
Juan Carlos Ospina Gonzalez

Trong câu hỏi của tôi và trong hầu hết các câu trả lời, chúng tôi đang cố gắng tránh những công việc không cần thiết. Nói cách khác: Những gì tôi cần làm chỉ là loại bỏ các nhánh, tôi sẽ không làm việc nữa. Loại bỏ repo, nhân bản nó và bắt đầu theo dõi các chi nhánh một lần nữa, là rất nhiều công việc không cần thiết đối với tôi. Ngoài ra, một số công việc có thể bị mất :(
Mailo Světel
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.