Làm thế nào tôi có thể biết nếu một cam kết là hậu duệ của một cam kết khác?


146

Với Git, làm thế nào tôi có thể biết nếu một cam kết trong chi nhánh của tôi là hậu duệ của một cam kết khác?


2
Câu hỏi tương tự đã hỏi ngược lại: stackoverflow.com/questions/18345157/
trộm

11
Bạn có thể thay đổi câu trả lời được chấp nhận của bạn? Đa số thích --is-ancestorgiải pháp.
Robert Siemer

Câu trả lời:


51

Nếu bạn muốn kiểm tra điều này theo chương trình (ví dụ: trong tập lệnh), bạn có thể kiểm tra xem git merge-base A Bcó bằng git rev-parse --verify A(thì A có thể truy cập từ B) hoặc nếu có git rev-parse --verify B(thì B có thể truy cập từ A). git rev-parseở đây cần thiết để chuyển đổi từ tên cam kết sang cam kết SHA-1 / id id.

Sử dụng git rev-listnhư trong câu trả lời VonC cũng có khả năng.

Chỉnh sửa: trong Git hiện đại có hỗ trợ rõ ràng cho truy vấn này ở dạng git merge-base --is-ancestor.


Nếu một trong những cam kết mà bạn đang hỏi là một mẹo chi nhánh , thì git branch --contains <commit>hoặc git branch --merged <commit>có thể là giải pháp phi lập trình tốt hơn.


1
Có thể cách nhanh nhất sẽ là git checkout -b quickcheck <more-recent-commit-ID>và sau đó git branch --contains <older-commit-ID>(và sau đó git branch -D quickcheckđể thoát khỏi nhánh tạm thời).
clee

2
Hai cách tiếp cận có thể, cả hai đều tệ hơn nhiều so với câu trả lời của @ MattR.
jwg

6
@jwg: Câu trả lời MattR tốt hơn, nhưng câu trả lời này (và có lẽ nó được chấp nhận) có trước git 1.8.0 và git merge-base --is-ancestorsau 2 năm.
Jakub Narębski

@ JakubNarębski Đủ rồi, xin lỗi.
jwg

2
Trong một kho lưu trữ lớn (2 triệu cam kết), tôi đã so sánh tốc độ git branch --contains <commit>git merge-base --is-ancestor ...: 3m40s so với 0.14s
hagello

259

Từ Git 1.8.0, điều này được hỗ trợ dưới dạng tùy chọn để merge-base:

git merge-base --is-ancestor <maybe-ancestor-commit> <descendant-commit>

Từ trang người đàn ông:

--is-tổ tiên

Kiểm tra xem cái đầu tiên là tổ tiên của cái thứ hai và thoát với trạng thái 0 nếu đúng hoặc với trạng thái 1 nếu không. Lỗi được báo hiệu bằng trạng thái khác không phải là 1.

Ví dụ:

git merge-base --is-ancestor origin/master master; echo $?

4
Đẹp! Đây là một tập lệnh shell kết thúc câu trả lời này trong một cái gì đó với đầu ra có thể thực hiện được của con người: gist.github.com/simonwhitaker/6354592
Simon Whitaker

1
Nói cách khác: git merge-base THING --is-ancestor OF_THING && echo yes || echo novd:git merge-base my-feature-branch --is-ancestor master && echo yes || echo no
user1735594 14/2/18

2
@smarber git merge-base --is-ancestor -- commit commithoạt động để băm ở bên tôi với git2.1.4 (Debian / Devuan 7.10 jessie) và 1.9.1 (Ubuntu 14.04 đáng tin cậy) hiện khá cổ xưa. Nó hoạt động ngay cả đối với Debian wheezy, nếu bạn làm sudo apt-get install git/wheezy-backports.
Tino

15

Loại hoạt động này dựa trên khái niệm phạm vi sửa đổi được nêu chi tiết trong câu hỏi SO: " Sự khác biệt trong 'git log origin / master' so với 'git log origin / master ..' ".

git rev-list sẽ có thể đi bộ trở lại từ một cam kết, cho đến khi khác nếu có thể đạt được.

Vì vậy, tôi sẽ cố gắng:

git rev-list --boundary 85e54e2408..0815fcf18a
0815fcf18a19441c1c26fc3495c4047cf59a06b9
8a1658147a460a0230fb1990f0bc61130ab624b2
-85e54e240836e6efb46978e4a1780f0b45516b20

(Cam kết ranh giới được bắt đầu bằng -)

Nếu lần xác nhận cuối cùng được hiển thị giống với lần xác nhận đầu tiên trong git rev-listlệnh, thì đó là lần xác nhận có thể truy cập được từ lần xác nhận thứ hai.

Nếu cam kết đầu tiên không thể truy cập từ lần thứ hai, git rev-listsẽ không trả lại gì.

git rev-list --boundary A..B

sẽ kết thúc bởi A, nếu Acó thể truy cập từ B.
Nó giống như:

git rev-list --boundary B --not A

, với Bmột tài liệu tham khảo tích cực , và Amột tài liệu tham khảo tiêu cực .
Nó sẽ bắt đầu tại Bvà quay trở lại qua biểu đồ cho đến khi nó gặp một bản sửa đổi có thể truy cập được A.
Tôi sẽ lập luận rằng nếu Acó thể truy cập trực tiếp từ Bnó, nó sẽ gặp phải (và hiển thị, vì --boundarytùy chọn) A.


Điều này nghe có vẻ như là một trường hợp sử dụng đủ phổ biến mà tôi ngạc nhiên rằng git chưa công bố lệnh "sứ" thực hiện chính xác điều này.
Lawrence I. Siden

1
@lsiden: đúng. Lưu ý bên lề: đừng quên rằng để kiểm tra một cái gì đó theo chương trình , bạn không nên sử dụng lệnh sứ, (như trong stackoverflow.com/questions/6976473/ ,), nhưng các lệnh hệ thống ống nước (như được minh họa trong stackoverflow.com/questions / 3878624 / Nhận )
VonC

Ôi trời, có vẻ như tôi phải quay lại và làm việc với các đoạn kịch bản Shell của mình!
Lawrence I. Siden

1
câu hỏi: tại sao -85e54e2...đoạn trích có một điểm trừ? cũng là một lỗi đánh máy có thể xảy ra: "... giống với lần cam kết đầu tiên ..."
sdaau 15/03/2015

1
@sdaau -có nghĩa là nó là một cam kết ranh giới. Tôi đã chỉnh sửa câu trả lời để làm cho nó rõ ràng hơn, cũng như để làm mới các liên kết doc và sửa lỗi chính tả cho câu trả lời 5 năm tuổi này.
VonC

11

Một cách khác là sử dụng git loggrep.

git log --pretty=format:%H abc123 | grep def456

Điều này sẽ tạo ra một dòng đầu ra nếu commit def456 là tổ tiên của commit abc123 hoặc không có đầu ra nào khác.

Bạn thường có thể thoát khỏi việc bỏ qua --prettyđối số, nhưng nó là cần thiết nếu bạn muốn đảm bảo rằng bạn chỉ tìm kiếm thông qua các băm cam kết thực tế và không thông qua các bình luận nhật ký, v.v.


Tôi dự kiến ​​giải pháp này sẽ chậm, nhưng nó thực sự khá nhanh, ngay cả đối với một dự án có 20k + cam kết
Renato Zannon

2
thay vì --prettytôi sử dụng --oneline: git log --oneline ce2ee3d | grep ec219cchoạt động tuyệt vời
gens

3

https://stackoverflow.com/a/13526591/895245 đề cập đến nó, bây giờ để làm cho nó thân thiện hơn với con người:

git-is-ancestor() (
  if git merge-base --is-ancestor "$1" "$2"; then
      echo 'ancestor'
  elif git merge-base --is-ancestor "$2" "$1"; then
      echo 'descendant'
  else
      echo 'unrelated'
  fi
)
alias giia='git-is-ancestor'

điều này sẽ trả về 'không liên quan' nếu cả hai tham số trỏ đến cùng một cam kết
mik

1

git show-nhánh nhánh-sha1 commit-sha1

Ở đâu:

  • nhánh-sha1: sha1 trong nhánh bạn muốn kiểm tra
  • commit-sha1: sha1 của cam kết bạn muốn kiểm tra

0

Nếu bạn đang sử dụng git merge-base --is-ancestor, hãy đảm bảo sử dụng Git 2.28 (Q3 2020)

Với Git 2.28 (quý 3 năm 2020), một vài trường trong " struct commit" không phải luôn luôn có mặt đã được chuyển sang cam kết.

Xem cam kết c752ad0 , cam kết c49c82a , cam kết 4844812 , cam kết 6da43d9 (17 tháng 6 năm 2020) của Abhishek Kumar ( abhishekkumar2718) .
(Được hợp nhất bởi Junio ​​C Hamano - gitster- trong cam kết d80bea4 , ngày 06 tháng 7 năm 2020)

commit-graph: giới thiệu commit_graph_data_slab

Đã ký tắt: Abhishek Kumar

Cấu trúc cam kết được sử dụng trong nhiều ngữ cảnh. Tuy nhiên, các thành viên generationgraph_poschỉ được sử dụng cho các hoạt động liên quan đến biểu đồ cam kết và nếu không sẽ lãng phí bộ nhớ.

Sự lãng phí này sẽ rõ rệt hơn khi chúng ta chuyển sang số thế hệ v2, sử dụng số thế hệ 64 bit thay vì 32 bit hiện tại.

Vì chúng thường được truy cập cùng nhau, hãy giới thiệu struct commit_graph_datavà chuyển chúng sang một commit_graph_datatấm.

Trong khi bộ kiểm tra tổng thể chạy nhanh như master, (sê-ri: 26m48 , master: 27m34, nhanh hơn 2,87%), một số lệnh nhất định như git merge-base --is-ancestorđã bị làm chậm 40% như được phát hiện bởi Seneder Gábor .
Sau khi giảm thiểu truy cập bản cam kết, tốc độ chậm vẫn tồn tại nhưng gần hơn với 20%.

Derrick Stolee tin rằng sự chậm lại là do thuật toán cơ bản chứ không phải là sự chậm chạp của truy cập bản cam kết và chúng tôi sẽ theo dõi trong loạt bài sau.


-1

Dựa trên câu trả lời của itub, trong trường hợp bạn cần làm điều này cho tất cả các thẻ trong kho lưu trữ:

for i in `git tag` ; do echo -ne $i "\t" ; git log --pretty=format:%H $i | (grep <commit to find> || echo ""); done
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.