Nhật ký Git để nhận các cam kết chỉ cho một chi nhánh cụ thể


214

Tôi muốn liệt kê tất cả các cam kết chỉ là một phần của một chi nhánh cụ thể.

Với những điều sau đây, nó liệt kê tất cả các cam kết từ chi nhánh, nhưng cũng từ cha mẹ (chủ)

git log mybranch

Tùy chọn khác mà tôi tìm thấy, là loại trừ các xác nhận có thể truy cập được bởi chủ và cung cấp cho tôi những gì tôi muốn, NHƯNG tôi muốn tránh sự cần thiết phải biết tên các nhánh khác.

git log mybranch --not master

Tôi đã cố gắng sử dụng git for-each-ref, nhưng nó cũng liệt kê mybranch vì vậy thực sự nó không bao gồm tất cả:

git log mybranch --not $(git for-each-ref --format '^%(refname:short)' refs/heads/)

Cập nhật:

Tôi đang thử nghiệm một tùy chọn mới mà tôi đã tìm thấy cách đây một thời gian và cho đến bây giờ dường như đây có thể là thứ tôi đang tìm kiếm:

git log --walk-reflogs mybranch

Cập nhật (2013/02/13T15: 08):

Tùy chọn --walk-reflogs là tốt, nhưng tôi đã kiểm tra xem có hết hạn cho reflog không (mặc định là 90 ngày, gc.reflogExpire ).

Tôi nghĩ rằng tôi đã tìm thấy câu trả lời tôi đang tìm kiếm:

git log mybranch --not $(git for-each-ref --format='%(refname)' refs/heads/ | grep -v "refs/heads/mybranch")

Tôi chỉ xóa chi nhánh hiện tại khỏi danh sách các chi nhánh có sẵn và sử dụng danh sách đó để loại trừ khỏi nhật ký. Bằng cách này, tôi chỉ nhận được các cam kết chỉ đạt được bởi mybranch .


có thể trùng lặp với stackoverflow.com/questions/53569
StarPinkER

Cũng đã thấy câu hỏi đó, nhưng không giống nhau
dimirc

2
Bạn có chắc chắn đó là những gì bạn muốn? Tôi thấy tốt hơn khi có một mục tiêu trong tâm trí: "những gì trong chi nhánh của tôi không nằm ở thượng nguồn" hoặc "những gì trong chi nhánh của tôi không phải là chủ". Thêm vào đó, trong khi git nhanh chóng cắt tỉa, điều này sẽ tốn kém hơn khi bạn có nhiều chi nhánh hơn. Tôi có một đoạn script tôi sử dụng mà tôi gọi là "git Mất tích" sau lệnh "mất tích" của bzr. Bạn có thể tìm thấy nó ở đây: github.com/jszakmeister/etc/blob/master/git-addons/git-missing .
John Szakmeister

1
Tôi thực sự cần cái này cho một cái móc sau nhận, vì vậy "chủ nhân" sẽ không luôn là chi nhánh để loại trừ
dimirc

Có, bản sao được đề cập bởi StarPinkER hoạt động tốt với tôi: git log $ (chi nhánh git merge-base) .. chi nhánh
charo

Câu trả lời:


165

Từ những gì có vẻ như bạn nên sử dụng cherry:

git cherry -v develop mybranch

Điều này sẽ hiển thị tất cả các cam kết có trong mybranch , nhưng KHÔNG phát triển . Nếu bạn rời khỏi tùy chọn cuối cùng ( mybranch ), nó sẽ so sánh chi nhánh hiện tại thay thế.

Như VonC đã chỉ ra, bạn LUÔN LUÔN so sánh chi nhánh của mình với chi nhánh khác, vì vậy hãy biết các chi nhánh của bạn và sau đó chọn chi nhánh nào để so sánh.


4
Đây chính xác là những gì tôi cần, nhưng có vẻ như cần phải có một lệnh trực quan hơn (có ngay cả trong thế giới của Git) cho việc này.
Seth

12
Bạn cũng có thể làm git cherry -v masterđể so sánh chi nhánh hiện tại của bạn với chi nhánh chính.
Pithikos

2
Điều nguy hiểm khi sử dụng git cherry, đó là các cam kết chỉ được khớp nếu các tệp khác nhau của chúng giống hệt nhau giữa các nhánh. Nếu bất kỳ loại hợp nhất nào được thực hiện sẽ làm cho sự khác biệt giữa một nhánh và nhánh kia, thì git cherry sẽ xem chúng như những cam kết khác nhau.
Ben

Điều này chỉ cho tôi fatal: Unknown commit mybranch.
Matt Arnold

1
@MattArnold bạn phải thay đổi văn bản "phát triển" và "mybranch" thành các nhánh tồn tại trên repo của bạn
Smilie

40

NHƯNG tôi muốn tránh sự cần thiết phải biết tên các chi nhánh khác.

Tôi không nghĩ điều này là có thể: một chi nhánh trong Git luôn dựa trên một chi nhánh khác hoặc ít nhất là trên một cam kết khác, như được giải thích trong " git diff không hiển thị đủ ":

nhập mô tả hình ảnh ở đây

Bạn cần một điểm tham chiếu cho nhật ký của bạn để hiển thị các cam kết đúng.

Như đã đề cập trong " GIT - Tôi đã phân nhánh từ đâu? ":

các nhánh chỉ đơn giản là con trỏ tới một số cam kết nhất định trong DAG

Vì vậy, ngay cả khi chỉ git log master..mybranchlà một câu trả lời, nó vẫn sẽ hiển thị quá nhiều cam kết, nếu mybranchdựa trên myotherbranch, chính nó dựa trên master.

Để tìm tham chiếu đó (nguồn gốc của chi nhánh của bạn), bạn chỉ có thể phân tích cú pháp và xem chúng thuộc nhánh nào, như đã thấy trong:


26

Cuối cùng tôi đã tìm ra cách để làm những gì OP muốn. Nó đơn giản như:

git log --graph [branchname]

Lệnh sẽ hiển thị tất cả các xác nhận có thể truy cập từ nhánh được cung cấp theo định dạng của biểu đồ. Nhưng, bạn có thể dễ dàng lọc tất cả các xác nhận trên nhánh đó bằng cách nhìn vào biểu đồ xác nhận có *ký tự đầu tiên trong dòng cam kết.

Ví dụ: chúng ta hãy xem đoạn trích git log --graph mastertrên repo của cakephp dưới đây:

D:\Web Folder\cakephp>git log --graph master
*   commit 8314c2ff833280bbc7102cb6d4fcf62240cd3ac4
|\  Merge: c3f45e8 0459a35
| | Author: José Lorenzo Rodríguez <lorenzo@users.noreply.github.com>
| | Date:   Tue Aug 30 08:01:59 2016 +0200
| |
| |     Merge pull request #9367 from cakephp/fewer-allocations
| |
| |     Do fewer allocations for simple default values.
| |
| * commit 0459a35689fec80bd8dca41e31d244a126d9e15e
| | Author: Mark Story <mark@mark-story.com>
| | Date:   Mon Aug 29 22:21:16 2016 -0400
| |
| |     The action should only be defaulted when there are no patterns
| |
| |     Only default the action name when there is no default & no pattern
| |     defined.
| |
| * commit 80c123b9dbd1c1b3301ec1270adc6c07824aeb5c
| | Author: Mark Story <mark@mark-story.com>
| | Date:   Sun Aug 28 22:35:20 2016 -0400
| |
| |     Do fewer allocations for simple default values.
| |
| |     Don't allocate arrays when we are only assigning a single array key
| |     value.
| |
* |   commit c3f45e811e4b49fe27624b57c3eb8f4721a4323b
|\ \  Merge: 10e5734 43178fd
| |/  Author: Mark Story <mark@mark-story.com>
|/|   Date:   Mon Aug 29 22:15:30 2016 -0400
| |
| |       Merge pull request #9322 from cakephp/add-email-assertions
| |
| |       Add email assertions trait
| |
| * commit 43178fd55d7ef9a42706279fa275bb783063cf34
| | Author: Jad Bitar <jadbitar@mac.com>
| | Date:   Mon Aug 29 17:43:29 2016 -0400
| |
| |     Fix `@since` in new files docblocks
| |

Như bạn có thể thấy, chỉ cam kết 8314c2ff833280bbc7102cb6d4fcf62240cd3ac4c3f45e811e4b49fe27624b57c3eb8f4721a4323b*ký tự đầu tiên trong các dòng cam kết. Những cam kết đó là từ nhánh chính trong khi bốn nhánh kia là từ một số nhánh khác.


11

Lệnh shell sau sẽ làm những gì bạn muốn:

git log --all --not $(git rev-list --no-walk --exclude=refs/heads/mybranch --all)

Hãy cẩn thận

Nếu bạn đã mybranchkiểm tra, lệnh trên sẽ không hoạt động. Đó là bởi vì các cam kết trên mybranchcũng có thể truy cập được HEAD, vì vậy Git không coi các cam kết là duy nhất mybranch. Để làm cho nó hoạt động khi mybranchđược kiểm tra, bạn cũng phải thêm một loại trừ cho HEAD:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=HEAD \
    --all)

Tuy nhiên, bạn không nên loại HEADtrừ trừ khi mybranchđược kiểm tra, nếu không bạn có nguy cơ hiển thị các cam kết không dành riêng cho mybranch.

Tương tự, nếu bạn có một nhánh từ xa có tên origin/mybranchtương ứng với mybranchnhánh cục bộ , bạn sẽ phải loại trừ nó:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=refs/remotes/origin/mybranch \
    --all)

Và nếu nhánh từ xa là nhánh mặc định cho kho lưu trữ từ xa (thường chỉ đúng với origin/master), bạn cũng sẽ phải loại trừ origin/HEAD:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=refs/remotes/origin/mybranch \
    --exclude=refs/remotes/origin/HEAD \
    --all)

Nếu bạn đã kiểm tra chi nhánh có một chi nhánh từ xa chi nhánh từ xa là mặc định cho kho lưu trữ từ xa, thì cuối cùng bạn sẽ loại trừ rất nhiều:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=HEAD
    --exclude=refs/remotes/origin/mybranch \
    --exclude=refs/remotes/origin/HEAD \
    --all)

Giải trình

Các git rev-listlệnh là một mức độ thấp (ống nước) lệnh đó đi các phiên bản nhất định và bãi các định SHA1 gặp phải. Hãy nghĩ về nó tương đương với git logngoại trừ nó chỉ hiển thị thông báo nhật ký SHA1, không tên tác giả, không dấu thời gian, không có nội dung "ưa thích" nào.

Các --no-walktùy chọn, như tên của nó, ngăn chặn git rev-listtừ đi bộ chuỗi gốc. Vì vậy, nếu bạn gõ, git rev-list --no-walk mybranchnó sẽ chỉ in một mã định danh SHA1: mã định danh của cam kết đầu của mybranchchi nhánh.

Các --exclude=refs/heads/mybranch --allđối số nói git rev-listđể bắt đầu từ mỗi tham chiếu ngoại trừ refs/heads/mybranch.

Vì vậy, khi bạn chạy git rev-list --no-walk --exclude=refs/heads/mybranch --all, Git sẽ in định danh SHA1 của cam kết tiền tip của mỗi ref trừ refs/heads/mybranch. Những cam kết này và tổ tiên của chúng là những cam kết mà bạn không quan tâm đến, đây là những cam kết bạn không muốn thấy.

Các cam kết khác là những cam kết bạn muốn xem, vì vậy chúng tôi thu thập kết quả đầu ra git rev-list --no-walk --exclude=refs/heads/mybranch --allvà bảo Git hiển thị mọi thứ trừ những cam kết đó và tổ tiên của chúng.

Đối --no-walksố là cần thiết cho các kho lưu trữ lớn (và là tối ưu hóa cho các kho nhỏ): Nếu không có nó, Git sẽ phải in và trình bao sẽ phải thu thập (và lưu trữ trong bộ nhớ) nhiều số nhận dạng cam kết hơn mức cần thiết. Với kho lưu trữ lớn, số lượng xác nhận được thu thập có thể dễ dàng vượt quá giới hạn đối số dòng lệnh của shell.

Lỗi Git?

Tôi đã mong đợi những điều sau đây để làm việc:

git log --all --not --exclude=refs/heads/mybranch --all

Nhưng nó không. Tôi đoán đây là một lỗi trong Git, nhưng có lẽ đó là cố ý.


Giải thích tốt đẹp. +1
VonC

5
Tôi đến đây muốn biết làm thế nào để làm Mercurial hg log -b <branch>. Tôi không hiểu tại sao mọi người nói git là không thân thiện. / s
weberc2

Tôi không biết tại sao git log --all --not --exclude=refs/heads/mybranch --allkhông hoạt động nhưng git log refs/heads/mybranch --not --exclude=refs/heads/mybranch --allthực hiện, với cùng một sự cảnh báo về việc loại trừ CHÍNH và nguồn gốc.
Ben C

8

Trả lời nhanh:

git log $(git merge-base master b2)..HEAD

Hãy cùng nói nào:

  1. Rằng bạn có một tổng thể chi nhánh

  2. Làm một vài cam kết

  3. Bạn đã tạo một nhánh có tên b2

  4. Làm git log -n1; Id cam kết là cơ sở hợp nhất giữa b2 và master

  5. Thực hiện một vài cam kết trong b2

  6. git log sẽ hiển thị lịch sử nhật ký của bạn về b2 và chủ

  7. Sử dụng phạm vi cam kết, nếu bạn không quen với khái niệm này, tôi mời bạn google nó hoặc xếp chồng lên nó,

    Đối với bối cảnh thực tế của bạn, bạn có thể làm ví dụ

    git log commitID_FOO..comitID_BAR
    

    ".." là toán tử phạm vi cho lệnh log.

    Điều đó có nghĩa là, trong một hình thức đơn giản, cung cấp cho tôi tất cả các bản ghi gần đây hơn so với commitID_FOO ...

  8. Nhìn vào điểm số 4, cơ sở hợp nhất

    Vì vậy: git log COMMITID_mergeBASE..HEADsẽ cho bạn thấy sự khác biệt

  9. Git có thể truy xuất cơ sở hợp nhất cho bạn như thế này

    git merge-base b2 master
    
  10. Cuối cùng bạn có thể làm:

    git log $(git merge-base master b2)..HEAD
    

4

Bạn có thể thử một cái gì đó như thế này:

#!/bin/bash

all_but()
{
    target="$(git rev-parse $1)"
    echo "$target --not"
    git for-each-ref --shell --format="ref=%(refname)" refs/heads | \
    while read entry
    do
        eval "$entry"

        test "$ref" != "$target" && echo "$ref"
    done
}

git log $(all_but $1)

Hoặc, mượn từ công thức trong Hướng dẫn sử dụng Git :

#!/bin/bash
git log $1 --not $( git show-ref --heads | cut -d' ' -f2 | grep -v "^$1" )

+1. Tôi đã viết trong câu trả lời của mình rằng bạn cần phân tích các cam kết để tìm nguồn gốc của chi nhánh và dường như bạn đã làm điều đó.
VonC

Bạn đã sử dụng log --walk-reflogs chưa? Tôi đang đọc về tùy chọn này và đang cho tôi kết quả tôi cần cho đến bây giờ, (vẫn đang thử nghiệm)
dimirc

@dimirc Reflogs là một con thú khác nhau. Nó ghi điểm chi nhánh theo thời gian, thường cho mục đích phục hồi. Tôi không chắc chắn những gì bạn đang làm trong hook sau nhận của bạn nhưng có lẽ nếu bạn giải thích nó, mọi người có thể cung cấp một câu trả lời hợp lý hơn cho vấn đề của bạn.
John Szakmeister

Tôi chỉ cần phân tích / kiểm tra tất cả các thông điệp cam kết trong một lần đẩy. Trường hợp tôi muốn trình bày là nếu ai đó đẩy nhiều lần xác nhận tại master và tạo một nhánh mới với nhiều lần xác nhận. Móc sau nhận cần kiểm tra các thay đổi được thực hiện cho cả ref / nhánh, ví dụ master và newbranch, tôi cần biết các ràng buộc cho nhánh mới để tránh phân tích cú pháp thông điệp cam kết hai lần, vì có thể được xác nhận từ master.
dimirc

1
Được chứ. Sau đó, tôi nghĩ rằng cách tiếp cận trên tôi đã vạch ra thực sự là những gì bạn muốn. Với reflog, các mục sẽ giảm sau một khoảng thời gian và tôi nghĩ rằng điều đó sẽ khiến bạn đau đầu. Bạn có thể muốn xem xét sử dụng git show-ref --tagsquá.
John Szakmeister

4
git rev-list --exclude=master --branches --no-walk

sẽ liệt kê các mẹo của mọi chi nhánh không master.

git rev-list master --not $(git rev-list --exclude=master --branches --no-walk)

sẽ liệt kê mọi cam kết trong masterlịch sử không có trong lịch sử của bất kỳ chi nhánh nào khác.

Trình tự rất quan trọng đối với các tùy chọn thiết lập đường ống lọc cho lựa chọn cam kết, do đó --branchesphải tuân theo bất kỳ mẫu loại trừ nào được áp dụng và --no-walkphải tuân theo các bộ lọc cung cấp cam kết danh sách không cần phải đi bộ.


4

Điều này sẽ xuất ra các cam kết trên nhánh hiện tại. Nếu bất kỳ đối số nào được thông qua, nó chỉ xuất ra các giá trị băm.

git_show_all_commits_only_on_this_branch

#!/bin/bash
function show_help()
{
  ME=$(basename $0)
  IT=$(cat <<EOF
  
  usage: $ME {NEWER_BRANCH} {OLDER_BRANCH} {VERBOSE}
  
  Compares 2 different branches, and lists the commits found only 
  in the first branch (newest branch). 

  e.g. 
  
  $ME         -> default. compares current branch to master
  $ME B1      -> compares branch B1 to master
  $ME B1 B2   -> compares branch B1 to B2
  $ME B1 B2 V -> compares branch B1 to B2, and displays commit messages
  
  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

# Show commit msgs if any arg passed for arg 3
if [ "$3" ]
then
  OPT="-v"
fi

# get branch names
OLDER_BRANCH=${2:-"master"}
if [ -z "$1" ]
then
  NEWER_BRANCH=$(git rev-parse --abbrev-ref HEAD)
else
  NEWER_BRANCH=$1
fi

if [ "$NEWER_BRANCH" == "$OLDER_BRANCH" ]
then
  echo "  Please supply 2 different branches to compare!"
  show_help
fi

OUT=$(\git cherry $OPT $OLDER_BRANCH $NEWER_BRANCH)

if [ -z "$OUT" ]
then
  echo "No differences found. The branches $NEWER_BRANCH and $OLDER_BRANCH are in sync."
  exit;
fi

if [ "$OPT" == "-v" ]
then
  echo "$OUT"
else
  echo "$OUT" | awk '{print $2}'
fi

1
Điều này tạo ra khoảng 100 thông điệp tường trình không liên quan cho tôi.
Arlie Stephens

Xin chào @ArlieStephens - Tôi đã thử nó và nó dường như vẫn hoạt động với tôi? Tôi chỉ cần thêm trợ giúp và nhiều thông báo lỗi hơn ở trên. Cho tôi biết làm thế nào nó đi!
Brad

Hấp dẫn. Nhìn vào mã như bạn có bây giờ, tôi nghĩ rằng nó có thể đơn giản như ban đầu giả định rằng chi nhánh tôi quan tâm đến từ chủ. Với mã hiện tại, tôi có thể đã chỉ định rõ ràng nhánh cha. (IIRC, chi nhánh dev của tôi đến từ một chi nhánh phát hành, không phải là chủ nhân.)
Arlie Stephens

@ArlieStephens - vâng Tôi không nghĩ rằng tôi đã thay đổi bất kỳ chức năng nào .... Tôi đã cập nhật nó một lần nữa và làm cho các tài liệu chính xác hơn một chút sau khi thử nghiệm cục bộ ... và thêm tùy chọn "dài dòng" trước đó, nhưng tôi đã không làm điều đó
Brad

3

Tôi đang sử dụng các lệnh sau:

git shortlog --no-merges --graph --abbrev-commit master..<mybranch>

hoặc là

git log --no-merges --graph --oneline --decorate master..<mybranch>

1

Tôi thấy cách tiếp cận này tương đối dễ dàng.

Thanh toán cho chi nhánh và hơn

  1. Chạy

    git rev-list --simplify-by-decoration -2 HEAD
    

Điều này sẽ chỉ cung cấp hai SHA:

1) cam kết cuối cùng của chi nhánh [C1]

2) và cam kết cha mẹ với cam kết đầu tiên của chi nhánh [C2]

  1. Bây giờ chạy

    git log --decorate --pretty=oneline --reverse --name-status <C2>..<C1>
    

Ở đây C1 và C2 là hai chuỗi bạn sẽ nhận được khi chạy lệnh đầu tiên. Đặt các giá trị này mà không có <> trong lệnh thứ hai.

Điều này sẽ đưa ra danh sách lịch sử thay đổi tập tin trong chi nhánh.


Nếu bạn đọc ba dòng đầu tiên của câu hỏi, bạn sẽ thấy tác giả liệt kê lệnh chính xác này và giải thích lý do tại sao đó không phải là thứ họ đang tìm kiếm
black_fm 18/07/18

Ah @black_fm Tôi đã thực hiện một số thay đổi trong câu trả lời. Bạn có thể bỏ phiếu nếu bạn thấy nó hữu ích ngay bây giờ. :)
Mustkeem K

0

Trong tình huống của tôi, chúng tôi đang sử dụng Git Flow và GitHub. Tất cả những gì bạn cần làm là: So sánh nhánh tính năng của bạn với nhánh phát triển của bạn trên GitHub.

Nó sẽ hiển thị các cam kết chỉ được thực hiện cho nhánh tính năng của bạn.

Ví dụ:

https://github.com/your_Vpo/compare/develop...feature_branch_name


Tôi đồng ý rằng khi bạn gặp phải tình huống "Tôi muốn xem yêu cầu kéo sẽ như thế nào", giải pháp tốt nhất thường là bỏ qua gitvà thay vào đó sử dụng GitHub để chỉ yêu cầu kéo hoặc so sánh các nhánh.
pkamb
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.