Sử dụng Git, hiển thị tất cả các cam kết tồn tại * chỉ * trên một nhánh cụ thể, chứ không phải * bất kỳ * nào khác


86

Với một nhánh, tôi muốn xem danh sách các cam kết chỉ tồn tại trên nhánh đó. Trong câu hỏi này, chúng ta thảo luận về các cách để xem cam kết nào nằm trên một nhánh nhưng không phải một hoặc nhiều nhánh khác được chỉ định.

Điều này hơi khác một chút. Tôi muốn xem cam kết nào nằm trên một nhánh nhưng không nằm trên bất kỳ nhánh nào khác.

Trường hợp sử dụng nằm trong chiến lược phân nhánh trong đó một số nhánh chỉ nên được hợp nhất và không bao giờ được cam kết trực tiếp trên. Điều này sẽ được sử dụng để kiểm tra xem có bất kỳ cam kết nào được thực hiện trực tiếp trên một nhánh "chỉ hợp nhất" hay không.

CHỈNH SỬA: Dưới đây là các bước để thiết lập repo git giả để kiểm tra:

git init
echo foo1 >> foo.txt
git add foo.txt
git commit -am "initial valid commit"
git checkout -b merge-only
echo bar >> bar.txt
git add bar.txt
git commit -am "bad commit directly on merge-only"
git checkout master
echo foo2 >> foo.txt 
git commit -am "2nd valid commit on master"
git checkout merge-only 
git merge master

Chỉ cam kết có thông báo "bad commit trực tiếp trên chỉ hợp nhất", được thực hiện trực tiếp trên nhánh chỉ hợp nhất, mới hiển thị.


1
Câu hỏi này giả định rằng tất cả các nhánh được hợp nhất từ ​​hiện đang có sẵn trên repo, không bao giờ bị xóa sau khi đã hợp nhất hoàn toàn và có thể không bao giờ được hợp nhất với tua nhanh. Hãy cho tôi biết nếu tôi thiếu cái gì đó, nhưng có vẻ như điều này chỉ khả thi đối với một nhóm tương đối nhỏ các nhánh hợp nhất được phép, vậy tại sao không chỉ sử dụng git log ^branch1 ^branch2 merge-only-branchcú pháp?
Karl Bielefeldt

1
Yêu git log ^branch1 ^branch2 merge-only-branchcầu liệt kê ra mọi chi nhánh. Điều đó có thể tránh được bằng một số cách sử dụng bash / grep thông minh (xem câu trả lời của tôi bên dưới), nhưng tôi hy vọng git có một số hỗ trợ tích hợp cho việc này. Bạn chính xác rằng nó cho rằng tất cả các nhánh hợp nhất từ ​​đều ở xa (chỉ cục bộ cũng tốt như không tồn tại đối với các nhà phát triển khác). Việc sử dụng --no-mergesbỏ qua bất kỳ cam kết nào đã được hợp nhất và sau đó đã xóa nhánh hợp nhất từ ​​ban đầu của chúng, vì vậy điều này giả định rằng các nhánh hợp nhất từ ​​được giữ nguyên cho đến khi chúng được hợp nhất thành một nhánh không hợp nhất chỉ-từ (tức là chính).
jimmyorr

Câu trả lời:


75

Chúng tôi vừa tìm thấy giải pháp thanh lịch này

git log --first-parent --no-merges

Trong ví dụ của bạn, tất nhiên cam kết ban đầu vẫn hiển thị.

câu trả lời này không trả lời chính xác câu hỏi, vì cam kết ban đầu vẫn hiển thị. Mặt khác, nhiều người đến đây dường như tìm thấy câu trả lời mà họ đang tìm kiếm.


1
Đơn giản, hiệu quả, tốt nhất.
Zabba

1
Vì cam kết ban đầu trên tổng thể vẫn hiển thị, điều này không trả lời câu hỏi.
jimmyorr

6
Chỉ điều này không thỏa mãn điều kiện "cam kết chỉ tồn tại trên nhánh đó" - nó hiển thị initial valid commit, là một phần của cả nhánh merge-onlymaster. Tuy nhiên, nếu người ta thực hiện nỗ lực đặt tên chi nhánh hiện tại ở cuối, theo sau là (các) tên chi nhánh có tiền tố ^ mà người ta biết chi nhánh hiện tại xuất phát, nó giải quyết được một nửa vấn đề (không bao gồm những thứ đã được hợp nhất). Ví dụ:git log --first-parent --no-merges merge-only ^master
Slipp D. Thompson

13
Tôi không chắc tại sao điều này được ủng hộ nhiều như vậy, nó dường như không phù hợp với câu hỏi chút nào. Nó chắc chắn không cung cấp thông tin mà người đăng đang tìm kiếm.
Chris Rasys

2
Câu trả lời này có thể không hoàn hảo. Nhưng nó đơn giản và chắc chắn hoạt động với một số phần mở rộng. Tôi thấy nó hữu ích để thêm tên chi nhánh - tức là lọc tất cả các cam kết thuộc về một chi nhánh đưa ra:git log --first-parent --no-merges | grep <branch_name>
artm

29

Lịch sự của người bạn thân yêu của tôi, Redmumba :

git log --no-merges origin/merge-only \
    --not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
    grep -Fv refs/remotes/origin/merge-only)

... origin/merge-onlytên chi nhánh chỉ hợp nhất từ ​​xa của bạn ở đâu . Nếu làm việc trên git repo chỉ dành cho cục bộ, hãy thay thế refs/remotes/originbằng refs/headsvà thay thế tên chi nhánh từ xa origin/merge-onlybằng tên chi nhánh cục bộ merge-only, tức là:

git log --no-merges merge-only \
    --not $(git for-each-ref --format="%(refname)" refs/heads |
    grep -Fv refs/heads/merge-only)

2
Tôi hy vọng ai đó có thể cung cấp giải pháp không sử dụng grep chỉ sử dụng git, nhưng nếu không, điều này có vẻ khá thanh lịch.
jimmyorr

1
Vâng, thanh lịch. Sử dụng git for-each-refđể liệt kê mọi tên tham chiếu trong nguồn gốc và grep -vbỏ qua nhánh chỉ hợp nhất. git logcó một --nottùy chọn, mà chúng tôi chuyển danh sách tất cả các tham chiếu (ngoại trừ nhánh chỉ hợp nhất). Nếu bạn có một câu trả lời thanh lịch hơn cho vấn đề, chúng ta hãy nghe nó.
jimmyorr

2
Ồ, tôi chắc đó là câu trả lời thanh lịch nhất. Tôi chỉ tranh luận rằng nó hơi "phức tạp / phức tạp" cho sự thanh lịch thực sự. :-) Không có ý chê bai cách tiếp cận của bạn, thưa bạn!
Chris K

1
Dấu vết /*trong các git for-each-refslệnh phụ thuộc vào việc không khớp với một số tệp hiện có và không có failglobhoặc nullglobthiết lập ( tùy chọn bash , các trình bao khác khác nhau). Bạn nên trích dẫn / loại bỏ các dấu hoa thị hoặc chỉ bỏ dấu gạch ngang /*( git for-each-refcác mẫu có thể khớp “từ đầu đến dấu gạch chéo”). Có thể sử dụng grep -Fv refs/remotes/origin/foo( refs/heads/foo) để nghiêm ngặt hơn về những giới thiệu bị loại bỏ.
Chris Johnsen

3
Nó có thể được đơn giản hóa nếu bạn chỉ muốn xem các cam kết hiện diện trong một nhánh chứ không phải trong nhánh khác git log --no-merges B1 --not B2:, trong đó B1 là nhánh mà bạn quan tâm và B2 là nhánh mà bạn muốn so sánh với B1. Cả B1 và ​​B2 đều có thể là các nhánh cục bộ hoặc từ xa, do đó bạn có thể chỉ định git log --no-merges master --not origin/masterhoặc thậm chí chỉ định hai nhánh từ xa.
mr.b

21
git log origin/dev..HEAD

Điều này sẽ cho bạn thấy tất cả các cam kết được thực hiện trong chi nhánh của bạn.


2
@Prakash origin/branchNamesẽ trỏ đến người đứng đầu chi nhánh từ xa VÀ HEADsẽ trỏ đến điểm giao dịch của cam kết cục bộ cuối cùng trong chi nhánh đó. Vì vậy, điều này sẽ không hoạt động khi bạn đã sử dụng git push.
Bharat

Bạn có thể sử dụng điều này để so sánh một chi nhánh địa phương khác. Cờ --no-merge cũng có thể hữu ích để giải quyết câu hỏi ban đầu của OP.
Paul Whipp

15

Câu trả lời @Prakash hoạt động. Chỉ để rõ ràng ...

git checkout feature-branch
git log master..HEAD

liệt kê các cam kết trên nhánh tính năng nhưng không liệt kê nhánh ngược dòng (thường là chính của bạn).


12

Có thể điều này có thể giúp:

git show-chi nhánh


2
Mặc dù về mặt lý thuyết, điều này có thể trả lời câu hỏi, nhưng tốt hơn hết bạn nên đưa các phần thiết yếu của câu trả lời vào đây và cung cấp liên kết để tham khảo.
Vladimir Panteleev

Điều này thực sự khá hữu ích. Xem stackoverflow.com/a/7623339/874188 để có ví dụ phức tạp hơn và một số câu trả lời liên quan.
tripleee

7

Thử đi:

git rev-list --all --not $(git rev-list --all ^branch)

Về cơ bản git rev-list --all ^branchnhận tất cả các bản sửa đổi không có trong nhánh và sau đó bạn lấy tất cả các bản sửa đổi trong repo và trừ danh sách trước đó là các bản sửa đổi chỉ trong nhánh.

Sau bình luận của @ Brian:

Từ tài liệu của git rev-list:

List commits that are reachable by following the parent links from the given commit(s)

Vì vậy, một lệnh như git rev-list Anơi A là một cam kết sẽ liệt kê các cam kết có thể truy cập được từ A bao gồm A.

Với ý nghĩ đó, một cái gì đó như

git rev-list --all ^A

sẽ liệt kê các cam kết không thể đạt được từ A

Vì vậy, git rev-list --all ^branchsẽ liệt kê tất cả các cam kết không thể truy cập từ đầu nhánh. Điều này sẽ loại bỏ tất cả các cam kết trong nhánh, hay nói cách khác là các cam kết chỉ có trong các nhánh khác.

Bây giờ chúng ta hãy đến với git rev-list --all --not $(git rev-list --all ^branch)

Điều này sẽ như thế nào git rev-list --all --not {commits only in other branches}

Vì vậy, chúng tôi muốn liệt kê allnhững thứ không thể truy cập được từall commits only in other branches

Đó là tập hợp các cam kết chỉ có trong nhánh. Hãy lấy một ví dụ đơn giản:

             master

             |

A------------B

  \

   \

    C--------D--------E

                      |

                      branch

Ở đây mục tiêu là lấy D và E, các cam kết không nằm trong nhánh nào khác.

git rev-list --all ^branch chỉ đưa ra B

Bây giờ, git rev-list --all --not Blà những gì chúng ta đi xuống. Mà cũng là git rev-list -all ^B- chúng tôi muốn tất cả các cam kết không thể truy cập được từ B. Trong trường hợp của chúng tôi, đó là D và E. Đó là những gì chúng tôi muốn.

Hy vọng điều này giải thích cách lệnh hoạt động chính xác.

Chỉnh sửa sau khi nhận xét:

git init
echo foo1 >> foo.txt
git add foo.txt
git commit -am "initial valid commit"
git checkout -b merge-only
echo bar >> bar.txt
git add bar.txt
git commit -am "bad commit directly on merge-only"
git checkout master
echo foo2 >> foo.txt 
git commit -am "2nd valid commit on master"

Sau các bước trên, nếu bạn thực hiện, git rev-list --all --not $(git rev-list --all ^merge-only)bạn sẽ nhận được cam kết mà bạn đang tìm kiếm - "bad commit directly on merge-only"một.

Nhưng một khi bạn thực hiện bước cuối cùng trong các bước của mình git merge master, lệnh sẽ không cho kết quả như mong đợi. Bởi vì hiện tại không có cam kết nào không có trong chỉ hợp nhất vì một cam kết bổ sung trong tổng thể cũng đã được hợp nhất để chỉ hợp nhất. Vì vậy, git rev-list --all ^branchđưa ra kết quả trống và do đó git rev-list -all --not $(git rev-list --all ^branch)sẽ cung cấp tất cả các cam kết trong chỉ hợp nhất.


1
Hm ... không chắc tại sao, nhưng điều này không hoàn toàn hiệu quả. Piping đầu ra lệnh của bạn để xargs -L 1 -t git branch -a --containshiển thị nhiều sai lệch (thực tế là các cam kết trên các nhánh khác). Tôi đã thử có và không có --no-merges. Cảm ơn vì đã trả lời!
jimmyorr

Có vẻ như hoạt động tốt đối với tôi theo như tôi có thể thấy trong một repo git giả.
manojlds

Tôi đã thêm các bước để tạo repo git giả để giúp giải thích vấn đề với câu trả lời của bạn.
jimmyorr

Ồ, rất tiếc. Đã ủng hộ điều này trước khi tôi nghĩ về nó. git rev-list --all ^branchsẽ cung cấp cho bạn tất cả các cam kết không có trong branch. Sau đó, bạn sẽ trừ số đó khỏi danh sách trong branch; nhưng theo định nghĩa, tất cả các cam kết không có trong branchđều không ở trong branch, vì vậy bạn sẽ không trừ bất cứ thứ gì. Những gì jimmyorr đang tìm kiếm là các cam kết nằm trong branchnhưng không phải master, cũng như bất kỳ nhánh nào khác. Bạn không muốn trừ các cam kết không có trong branch; bạn muốn trừ các cam kết trong bất kỳ nhánh nào khác.
Brian Campbell

1
@manojlds "(tất cả các bản sửa đổi) - (tất cả các bản sửa đổi không có trong nhánh) = bản sửa đổi trong nhánh." Có, điều đó hoạt động để đưa tất cả các bản sửa đổi vào branch, nhưng cũng vậy git rev-list branch. Bạn chỉ đang viết git rev-list branchmột cách phức tạp hơn (và chậm hơn). Nó không hoạt động để trả lời câu hỏi, đó là cách tìm tất cả các cam kết branch trong bất kỳ nhánh nào khác .
Brian Campbell

2

Một biến thể khác của các câu trả lời được chấp nhận, để sử dụng với master

git log origin/master --not $(git branch -a | grep -Fv master)

Lọc tất cả các cam kết xảy ra trong bất kỳ nhánh nào khác ngoài nhánh chính.


0

Đây không chính xác là một câu trả lời thực sự, nhưng tôi cần quyền truy cập vào định dạng và rất nhiều không gian. Tôi sẽ cố gắng mô tả lý thuyết đằng sau những gì tôi cho là hai câu trả lời tốt nhất: câu trả lời được chấp nhận và câu trả lời được xếp hạng hàng đầu (ít nhất là hiện tại) . Nhưng trên thực tế, họ trả lời các câu hỏi khác nhau .

Các cam kết trong Git rất thường "trên" nhiều nhánh tại một thời điểm. Thật vậy, đó là phần lớn những gì câu hỏi là về. Được:

...--F--G--H   <-- master
         \
          I--J   <-- develop

trong đó các chữ cái viết hoa đại diện cho ID băm Git thực tế, chúng tôi thường tìm kiếm chỉ cam kếtH hoặc chỉ cam kếtI-J trong git logđầu ra của chúng tôi . Cam kết thông qua Gđều nằm trên cả hai nhánh, vì vậy chúng tôi muốn loại trừ chúng.

(Lưu ý rằng trong các biểu đồ được vẽ như thế này, các cam kết mới hơn hướng về bên phải. Các tên chọn một cam kết ngoài cùng bên phải trên dòng đó. Mỗi cam kết đó có một cam kết mẹ, là cam kết ở bên trái của chúng: cha của HG, và cha mẹ của JI. Cha mẹ của lại IG. Cha mẹ của GFFcó cha mẹ đơn giản không được hiển thị ở đây: nó là một phần của ...phần.)

Đối với trường hợp đặc biệt đơn giản này, chúng ta có thể sử dụng:

git log master..develop    # note: two dots

để xem I-J, hoặc:

git log develop..master    # note: two dots

chỉ để xem H. Tên bên phải, sau hai dấu chấm, nói với Git: vâng, những cam kết này . Tên bên trái, trước hai dấu chấm, nói với Git: không, không phải những cam kết này . Git bắt đầu ở cuối —at commit Hhoặc commit J—và hoạt động ngược lại . Để biết thêm (nhiều) về điều này, hãy xem Think Like (a) Git .

Cách câu hỏi ban đầu được diễn đạt, mong muốn là tìm các cam kết có thể truy cập được từ một tên cụ thể, nhưng không phải từ bất kỳ tên nào khác trong cùng danh mục chung đó. Đó là, nếu chúng ta có một đồ thị phức tạp hơn:

               O--P   <-- name5
              /
             N   <-- name4
            /
...--F--G--H--I---M   <-- name1
         \       /
          J-----K   <-- name2
           \
            L   <-- name3

chúng ta có thể chọn một trong những tên này, chẳng hạn như name4hoặc name3, và hỏi: tên đó có thể tìm thấy cam kết nào, nhưng không phải tên nào khác? Nếu chúng tôi chọn name3câu trả lời là cam kết L. Nếu chúng ta chọn name4, câu trả lời là không có cam kết nào cả: cam kết mà name4tên là cam kết Nnhưng Ncó thể tìm thấy cam kết bằng cách bắt đầu tại name5và làm việc ngược lại.

Câu trả lời được chấp nhận hoạt động với tên theo dõi từ xa, thay vì tên chi nhánh và cho phép bạn chỉ định một tên — tên được đánh vần origin/merge-only— làm tên đã chọn và xem xét tất cả các tên khác trong không gian tên đó. Nó cũng tránh hiển thị các hợp nhất: nếu chúng tôi chọn name1là "tên thú vị" và nói cho tôi biết các cam kết có thể truy cập được từ name1chứ không phải bất kỳ tên nào khác , chúng tôi sẽ thấy cam kết hợp nhất Mcũng như cam kết thông thường I.

Câu trả lời phổ biến nhất là khá khác nhau. Đó là tất cả về việc duyệt qua biểu đồ cam kết mà không theo cả hai chân của hợp nhất và không hiển thị bất kỳ cam kết nào được hợp nhất. name1Ví dụ: nếu chúng tôi bắt đầu với , chúng tôi sẽ không hiển thị M(đó là hợp nhất), nhưng giả sử cha mẹ đầu tiên của hợp nhất Mlà cam kết I, chúng tôi thậm chí sẽ không xem xét các cam kết JK. Chúng tôi sẽ kết thúc cho thấy cam kết I, và cũng cam kết H, G, F, và vân vân-không ai trong số đó là những cam kết hợp nhất và tất cả đều có thể truy cập bằng cách bắt đầu ở Mvà làm việc ngược, tham quan chỉ đầu tiên mẹ của mỗi hợp nhất cam kết.

Ví dụ, câu trả lời phổ biến nhất khá phù hợp với việc xem xét thời masterđiểm masterdự định trở thành một nhánh chỉ hợp nhất. Nếu tất cả "công việc thực sự" được thực hiện trên các nhánh phụ mà sau đó được hợp nhất vào master, chúng ta sẽ có một mẫu như sau:

I---------M---------N   <-- master
 \       / \       /
  o--o--o   o--o--o

trong đó tất cả các ocam kết không có tên chữ cái là các cam kết thông thường (không hợp nhất) MNlà các cam kết hợp nhất. Cam kết Ilà cam kết ban đầu: cam kết đầu tiên từng được thực hiện và là cam kết duy nhất phải ở chế độ chính mà không phải là cam kết hợp nhất. Nếu git log --first-parent --no-merges masterhiển thị bất kỳ cam kết nào khác I , chúng tôi có một tình huống như sau:

I---------M----*----N   <-- master
 \       / \       /
  o--o--o   o--o--o

nơi chúng tôi muốn xem cam kết *đã được thực hiện trực tiếp master, không phải bằng cách hợp nhất một số nhánh tính năng.

Nói tóm lại, câu trả lời phổ biến là tuyệt vời để xem masterkhi nào masterđược dùng để chỉ hợp nhất, nhưng không tuyệt vời cho các tình huống khác. Câu trả lời được chấp nhận phù hợp với những tình huống khác này.

Tên theo dõi từ xa có giống như tên origin/master chi nhánh không?

Một số phần của Git nói rằng chúng không phải là:

git checkout master
...
git status

nói on branch master, nhưng:

git checkout origin/master
...
git status

nói HEAD detached at origin/master. Tôi thích đồng ý với git checkout/ git switch: origin/masterkhông phải là tên chi nhánh bởi vì bạn không thể "trên" nó.

Câu trả lời được chấp nhận sử dụng tên theo dõi từ xa origin/*làm "tên chi nhánh":

git log --no-merges origin/merge-only \
    --not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
    grep -Fv refs/remotes/origin/merge-only)

Dòng giữa, gọi git for-each-ref, lặp lại các tên theo dõi từ xa cho điều khiển từ xa được đặt tên origin.

Lý do đây là một giải pháp tốt cho vấn đề ban đầu là ở đây chúng tôi quan tâm đến tên chi nhánh của người khác , hơn là tên chi nhánh của chúng tôi . Nhưng điều đó có nghĩa là chúng tôi đã định nghĩa chi nhánh là một cái gì đó khác với tên chi nhánh của chúng tôi . Tốt thôi: chỉ cần lưu ý rằng bạn đang làm điều này, khi bạn làm điều đó.

git log duyệt qua một số phần của biểu đồ cam kết

Những gì chúng tôi thực sự đang tìm kiếm ở đây là một loạt những gì tôi đã gọi là con dao nhỏ: hãy xem Chính xác chúng ta có nghĩa là gì khi "chi nhánh"? Đó là, chúng tôi đang tìm kiếm các đoạn trong một số tập hợp con của biểu đồ cam kết tổng thể .

Bất cứ khi nào chúng ta có Git xem xét tên chi nhánh như master, tên thẻ v2.1, hoặc tên theo dõi từ xa origin/master, chúng ta có xu hướng muốn Git cho chúng ta biết về cam kết đó mọi cam kết mà chúng ta có thể nhận được từ cam kết đó: bắt đầu từ đó và làm việc ngược lại.

Trong toán học, điều này được gọi là đi bộ đồ thị . Đồ thị cam kết của Git là Đồ thị vòng tròn được hướng dẫn hoặc DAG và loại đồ thị này đặc biệt thích hợp để đi bộ. Khi xem một biểu đồ như vậy, người ta sẽ ghé thăm từng đỉnh của biểu đồ có thể truy cập được thông qua đường dẫn đang được sử dụng. Các đỉnh trong biểu đồ Git là các cam kết, với các cạnh là các cung — liên kết một chiều — đi từ mỗi con đến mỗi cha. (Đây là lúc Think Like (a) Git xuất hiện. Bản chất một chiều của cung có nghĩa là Git phải hoạt động ngược lại, từ con đến cha.)

Hai lệnh Git chính để đi đồ thị là git loggit rev-list. Các lệnh này cực kỳ giống nhau - trên thực tế, chúng hầu hết được xây dựng từ các tệp nguồn giống nhau - nhưng đầu ra của chúng khác nhau: git logtạo ra đầu ra cho con người đọc, trong khi git rev-listtạo đầu ra dành cho các chương trình Git khác đọc. 1 Cả hai lệnh đều thực hiện kiểu đi bộ đồ thị này.

Hành trình biểu đồ mà họ thực hiện cụ thể là: đưa ra một số tập hợp các cam kết điểm bắt đầu (có lẽ chỉ một cam kết, có lẽ là một loạt các ID băm, có lẽ là một loạt các tên phân giải thành ID băm), đi qua biểu đồ, truy cập các cam kết . Các lệnh cụ thể, chẳng hạn như --nothoặc một tiền tố ^, hoặc --ancestry-path, hoặc --first-parent, sửa đổi đường đi của biểu đồ theo một cách nào đó.

Khi họ thực hiện bước biểu đồ, họ truy cập từng cam kết. Nhưng họ chỉ in một số tập hợp con được chọn của các cam kết đã đi bộ. Các chỉ thị chẳng hạn như --no-mergeshoặc --before <date>cho mã đi bộ đồ thị cam kết in .

Để thực hiện việc truy cập này, hãy cam kết một lần, hai lệnh này sử dụng một hàng đợi ưu tiên . Bạn chạy git loghoặc git rev-listvà cung cấp cho nó một số cam kết xuất phát điểm. Họ đặt những cam kết đó vào hàng đợi ưu tiên. Ví dụ, một đơn giản:

git log master

biến tên masterthành một ID băm thô và đặt một ID băm đó vào hàng đợi. Hoặc là:

git log master develop

biến cả hai tên thành ID băm và — giả sử đây là hai ID băm khác nhau — đặt cả hai vào hàng đợi.

Mức độ ưu tiên của các cam kết trong hàng đợi này được xác định bởi nhiều đối số hơn. Ví dụ: đối số --author-date-ordercho biết git loghoặc git rev-listsử dụng dấu thời gian của tác giả , thay vì dấu thời gian của người xác nhận. Mặc định là sử dụng dấu thời gian của người xác nhận và chọn lần cam kết mới nhất theo ngày: cái có số ngày cao nhất. Vì vậy, với master developgiả sử những cam kết này giải quyết cho hai cam kết khác nhau, Git sẽ hiển thị bất kỳ cái nào đến sau trước, bởi vì cái đó sẽ ở đầu hàng đợi.

Trong mọi trường hợp, mã đi bộ sửa đổi hiện chạy trong một vòng lặp:

  • Trong khi có các cam kết trong hàng đợi:
    • Loại bỏ mục nhập hàng đợi đầu tiên.
    • Quyết định có in cam kết này hay không. Ví dụ --no-merges,: không in gì nếu nó là một cam kết hợp nhất; --before: không in gì nếu ngày của nó không đến trước thời gian đã định. Nếu quá trình in không bị chặn, hãy in commit: for git log, show log của nó; cho git rev-list, in ID băm của nó.
    • Đặt một số hoặc tất cả các commit gốc của commit này vào hàng đợi (miễn là nó không có ở đó bây giờ và chưa được truy cập 2 ). Mặc định bình thường là đặt tất cả các bậc cha mẹ. Việc sử dụng --first-parentngăn chặn tất cả ngoại trừ cha đầu tiên của mỗi hợp nhất.

(Cả hai git logvà đều git rev-listcó thể thực hiện đơn giản hóa lịch sử có hoặc không có viết lại của cha mẹ vào thời điểm này, nhưng chúng tôi sẽ bỏ qua điều đó ở đây.)

Đối với một chuỗi đơn giản, như bắt đầu tại HEADvà hoạt động ngược lại khi không có cam kết hợp nhất, hàng đợi luôn có một cam kết trong đó ở đầu vòng lặp. Có một cam kết, vì vậy chúng tôi bật nó ra và in nó ra và đưa cha mẹ (đơn) của nó vào hàng đợi và đi vòng lại, và chúng tôi theo chuỗi ngược lại cho đến khi đạt được cam kết đầu tiên hoặc người dùng cảm thấy mệt mỏi với git logđầu ra và thoát chương trình. Trong trường hợp này, không có tùy chọn đặt hàng nào quan trọng: chỉ có một cam kết hiển thị.

Khi có các hợp nhất và chúng tôi theo dõi cả cha mẹ — cả hai “chân” của hợp nhất — hoặc khi bạn cung cấp git loghoặc git rev-listnhiều hơn một cam kết bắt đầu, các tùy chọn sắp xếp sẽ quan trọng.

Cuối cùng, hãy xem xét hiệu ứng của --nothoặc ^trước một trình xác định cam kết. Có một số cách để viết chúng:

git log master --not develop

hoặc là:

git log ^develop master

hoặc là:

git log develop..master

tất cả đều có nghĩa giống nhau. Các --notgiống như tiền tố ^ngoại trừ việc nó được áp dụng cho nhiều hơn một tên:

git log ^branch1 ^branch2 branch3

có nghĩa là không phải nhánh1, không phải nhánh2, có nhánh3; nhưng:

git log --not branch1 branch2 branch3

có nghĩa là không phải nhánh1, không phải nhánh2, không phải nhánh3 và bạn phải sử dụng một giây --notđể tắt nó:

git log --not branch1 branch2 --not branch3

đó là một chút khó xử. Hai lệnh "not" được kết hợp thông qua XOR, vì vậy nếu bạn thực sự muốn, bạn có thể viết:

git log --not branch1 branch2 ^branch3

có nghĩa là không phải nhánh1, không phải nhánh2 , có nhánh3 , nếu bạn muốn xáo trộn .

Tất cả những điều này đều hoạt động bằng cách ảnh hưởng đến bước đi của đồ thị. Khi git loghoặc git rev-listđi qua biểu đồ, nó đảm bảo không đưa vào hàng đợi ưu tiên bất kỳ cam kết nào có thể truy cập được từ bất kỳ tham chiếu phủ định nào . (Trên thực tế, chúng cũng ảnh hưởng đến thiết lập bắt đầu: các cam kết bị phủ định không thể đi vào hàng đợi ưu tiên ngay từ dòng lệnh, vì vậy git log master ^master, chẳng hạn như không hiển thị gì.)

Tất cả các cú pháp ưa thích được mô tả trong tài liệu gitrevisions đều sử dụng điều này và bạn có thể hiển thị điều này bằng một cuộc gọi đơn giản tới git rev-parse. Ví dụ:

$ git rev-parse origin/pu...origin/master     # note: three dots
b34789c0b0d3b137f0bb516b417bd8d75e0cb306
fc307aa3771ece59e174157510c6db6f0d4b40ec
^b34789c0b0d3b137f0bb516b417bd8d75e0cb306

Cú pháp ba chấm có nghĩa là các cam kết có thể truy cập được từ bên trái hoặc bên phải, nhưng loại trừ các cam kết có thể truy cập được từ cả hai . Trong trường hợp này, bản origin/mastercam kết, b34789c0bcó thể truy cập được từ origin/pu( fc307aa37...), do đó, origin/mastermã băm xuất hiện hai lần, một lần với một phủ định, nhưng trên thực tế, Git đạt được cú pháp ba chấm bằng cách đưa vào hai tham chiếu tích cực — hai ID băm không bị phủ định — và một âm một, được biểu thị bằng ^tiền tố.

Simiarly:

$ git rev-parse master^^@
2c42fb76531f4565b5434e46102e6d85a0861738
2f0a093dd640e0dad0b261dae2427f2541b5426c

Các ^@phương tiện cú pháp tất cả các bậc cha mẹ của các trao cam kết , và master^bản thân-phụ huynh đầu tiên của các cam kết được lựa chọn theo ngành tên master-là một hợp nhất cam kết, vì vậy nó có hai cha mẹ. Đây là hai phụ huynh. Và:

$ git rev-parse master^^!
0b07eecf6ed9334f09d6624732a4af2da03e38eb
^2c42fb76531f4565b5434e46102e6d85a0861738
^2f0a093dd640e0dad0b261dae2427f2541b5426c

Các ^!phương tiện hậu tố cam kết chính nó, nhưng không ai trong số các bậc cha mẹ của nó . Trong trường hợp này, master^0b07eecf6.... Chúng tôi đã thấy cả cha và mẹ với ^@hậu tố; chúng ở đây một lần nữa, nhưng lần này, bị phủ định.


1 Nhiều chương trình Git chạy theo đúng nghĩa đen git rev-listvới các tùy chọn khác nhau và đọc đầu ra của nó, để biết những gì cam kết và / hoặc các đối tượng Git khác sẽ sử dụng.

2 Bởi vì biểu đồ có dạng xoay vòng , nên có thể đảm bảo rằng chưa có ai được truy cập, nếu chúng ta thêm ràng buộc sẽ không bao giờ hiển thị cha mẹ trước khi hiển thị tất cả các con của nó ở mức ưu tiên. --date-order, --author-date-order--topo-orderthêm ràng buộc này. Thứ tự sắp xếp mặc định — không có tên — không. Nếu dấu thời gian cam kết không ổn định — ví dụ: nếu một số cam kết được thực hiện "trong tương lai" bởi một máy tính có đồng hồ bị tắt — điều này trong một số trường hợp có thể dẫn đến kết quả trông kỳ lạ.


Nếu bạn đã làm được như vậy, bây giờ bạn biết rất nhiều về git log

Tóm lược:

  • git log là hiển thị một số cam kết đã chọn trong khi xem một số hoặc tất cả một số phần của biểu đồ.
  • Các --no-mergesđối số, được tìm thấy trong cả hai chấp nhận và các câu trả lời hiện-top xếp hạng, ngăn chặn hiển thị một số cam kết rằng đi.
  • Đối --first-parentsố, từ câu trả lời hiện đang được xếp hạng cao nhất, ngăn chặn việc đi một số phần của biểu đồ, trong chính quá trình đi bộ của biểu đồ.
  • Các --nottiền tố để đối số dòng lệnh, như được sử dụng trong các câu trả lời được chấp nhận, ngăn chặn từng đến thăm một số bộ phận của đồ thị ở tất cả, ngay từ đầu.

Chúng tôi nhận được câu trả lời mà chúng tôi thích, cho hai câu hỏi khác nhau, bằng cách sử dụng các tính năng này.

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.