Tìm cam kết hợp nhất bao gồm một cam kết cụ thể


183

Hãy tưởng tượng lịch sử sau đây:

       c---e---g--- feature
      /         \
-a---b---d---f---h--- master

Làm thế nào tôi có thể tìm thấy khi cam kết "c" đã được hợp nhất thành chủ (nghĩa là tìm cam kết hợp nhất "h")?

Câu trả lời:


157

Ví dụ của bạn cho thấy chi nhánh featurevẫn có sẵn.

Trong trường hợp đó hlà kết quả cuối cùng của:

git log master ^feature --ancestry-path

Nếu chi nhánh featurekhông còn khả dụng nữa, bạn có thể hiển thị các cam kết hợp nhất trong dòng lịch sử giữa cmaster:

git log <SHA-1_for_c>..master --ancestry-path --merges

Tuy nhiên, điều này cũng sẽ hiển thị tất cả các sự hợp nhất đã xảy ra sau hvà giữa egtrên feature.


So sánh kết quả của các lệnh sau:

git rev-list <SHA-1_for_c>..master --ancestry-path

git rev-list <SHA-1_for_c>..master --first-parent

sẽ cung cấp cho bạn SHA-1 hlà hàng cuối cùng.

Nếu bạn có sẵn, bạn có thể sử dụng comm -1 -2trên các kết quả này. Nếu bạn đang sử dụng msysgit, bạn có thể sử dụng mã perl sau đây để so sánh:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

(mã perl từ http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , lấy từ "ai đó trong nhóm tin comp.unix.shell").

Xem thay thế quá trình nếu bạn muốn làm cho nó một lót.


4
Ngay cả khi bạn có comm, bạn không thể sử dụng nó vì đầu ra của lệnh "git rev-list" không được sắp xếp theo từ vựng. Tất nhiên bạn có thể sắp xếp đầu ra của mỗi lệnh trước khi tìm kiếm các dòng chung, nhưng sau đó, cam kết mong muốn không nhất thiết phải là lệnh cuối cùng. Vì vậy, tôi nghĩ rằng một cái gì đó như lệnh perl (mặc dù tối nghĩa) là cần thiết.
mhagger

12
Tôi vừa viết một kịch bản git-khi được hợp nhất thực hiện đề xuất này (với khá nhiều tính năng khác). Xem github.com/mhagger/git-when-merged
mhagger

2
Giả sử tại một thời điểm nào đó masterđược sáp nhập vào feature, sau đó ngay lập tức featuređược hợp nhất thành mastermột chuyển tiếp nhanh (mẹo featurethay thế master). Điều đó sẽ gây ra --first-parenttrả lại sai cha mẹ?
Kelvin

4
Tôi đã thử comm -1 -2nhưng nó không hoạt động. commchỉ hoạt động trên các dòng được sắp xếp. (Công cụ một lớp perl hoạt động, mặc dù tôi không thể đọc nó.)
Domon

2
Điều này hơi muộn nhưng việc thêm vào để mọi người có thể thấy nó hữu ích vì git tự nhiên không cung cấp điều này. Có một số trường hợp góc mà tôi nghĩ rằng câu trả lời này không xử lý (xem câu trả lời của tôi bên dưới stackoverflow.com/a/43716029/58678 xử lý hầu hết các trường hợp góc). Dưới đây là các trường hợp góc: git find-merge h master(trả về không có gì nhưng nên trả về h), git find-merge d master(trả về f nhưng nên trả về d), git find-merge c feature(trả về e nhưng nên trả về g).
hIpPy

133

Thêm phần này vào ~/.gitconfig:

[alias]
    find-merge = "!sh -c 'commit=$0 && branch=${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'"
    show-merge = "!sh -c 'merge=$(git find-merge $0 $1) && [ -n \"$merge\" ] && git show $merge'"

Sau đó, bạn có thể sử dụng các bí danh như thế này:

# current branch
git find-merge <SHA-1>
# specify master
git find-merge <SHA-1> master

Để xem thông điệp của cam kết hợp nhất và các chi tiết khác, hãy sử dụng git show-mergevới cùng các đối số.

(Dựa trên câu trả lời của Gauthier . Cảm ơn Rosen Matevjavabrett đã sửa lỗi sort.)


6
Xinh đẹp! Đây là giải pháp thả tốt nhất.
N Jones

1
Coi chừng sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2không tìm thấy chính xác hàng cuối cùng. Đây là một ví dụ khi nó thất bại.
Rosen Matev

1
@RosenMatev Nó tìm thấy 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7ở đây khi tôi chạy nó trên dán của bạn, điều đó không được mong đợi? Bạn đang dùng hệ điều hành nào?
cướp

1
@robinst, của bạn là chính xác. Tôi đã nhận được "(GNU coreutils) 8.4" và đối với tôi, nó tìm thấy29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev

2
@powlo: Không có gì, nó chỉ là một lệnh thay vì hai lệnh cho thuận tiện.
cướp

28

git-get-merge sẽ định vị và hiển thị cam kết hợp nhất mà bạn đang tìm kiếm:

pip install git-get-merge
git get-merge <SHA-1>

Lệnh theo các con của cam kết đã cho cho đến khi hợp nhất vào một nhánh khác (có lẽ là chủ) được tìm thấy.


22

Đó là, để tóm tắt bài đăng của Gauthier:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' <(git rev-list --ancestry-path <SHA-1_for_c>..master) <(git rev-list --first-parent <SHA-1_for_c>..master) | tail -n 1

EDIT: bởi vì điều này sử dụng thay thế quá trình " <()", nó không tương thích POSIX và nó có thể không hoạt động với trình bao của bạn. Nó hoạt động với bashhoặc zshmặc dù.


4
Tôi không thường xuyên sao chép / dán mã từ internet, nhưng khi tôi làm nó hoạt động hoàn hảo! Cảm ơn Totor đã cho phép tôi không suy nghĩ.
Ben

2
@ TheodoreR.Smith không may, cú pháp <()không tương thích POSIX. Bạn cần sử dụng bash, zshhoặc một vỏ hỗ trợ quá trình thay thế . Tôi chỉnh sửa câu trả lời của tôi cho phù hợp.
Totor

Không làm việc cho tôi. Tôi nghĩ rằng nhánh tính năng của tôi trước tiên được hợp nhất với một nhánh khác trước khi nó được hợp nhất thành chủ. Điều đó có thể giải thích tại sao các lệnh của bạn dẫn đến "Hợp nhất nhánh 'release_branch'" chứ không phải là "Hợp nhất nhánh 'Feature_branch'".
Tim Kuipers

14

Tôi cần phải làm điều này, và bằng cách nào đó đã tìm thấy git-when-merged(thực sự tham khảo câu hỏi SO này, nhưng Michael Haggerty không bao giờ thêm một tài liệu tham khảo cho kịch bản Python rất hay của mình ở đây). Vì vậy, bây giờ tôi có.


Tôi thực sự đến câu hỏi này từ trang của anh ấy.
Flavius

12

Dựa trên câu trả lời tuyệt vời của Gauthier, chúng tôi không cần sử dụng commđể so sánh các danh sách. Vì chúng tôi đang tìm kiếm kết quả cuối cùng --ancestry-pathcũng nằm trong đó --first-parent, chúng tôi chỉ có thể grep cho kết quả sau trong kết quả đầu ra:

git rev-list <SHA>..master --ancestry-path | grep -f <(git rev-list <SHA>..master --first-parent) | tail -1

Hoặc đối với một cái gì đó linh hoạt và có thể tái sử dụng, đây là một chức năng để bật vào .bashrc:

function git-find-merge() {
  git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}

Điều này làm việc cho tôi. commkhông hoạt động khi đầu vào không được sắp xếp.
hIpPy

4

Đối với đám đông Ruby, có git-whence . Rất dễ.

$ gem install git-whence
$ git whence 1234567
234557 Merge pull request #203 from branch/pathway

4

Tôi sử dụng tập lệnh bash bên dưới mà tôi đặt tại đường dẫn ~/bin/git-find-merge. Nó dựa trên câu trả lời của Gauthiercâu trả lời của evilstreak với một vài điều chỉnh để xử lý các trường hợp góc. commném khi đầu vào không được sắp xếp. grep -fhoạt động hoàn hảo.

Trường hợp góc:

  • Nếu cam kết là một cam kết hợp nhất trên đường dẫn cha mẹ đầu tiên của nhánh, thì trả về cam kết.
  • Nếu cam kết là một cam kết hợp nhất NON trên đường dẫn cha mẹ đầu tiên của nhánh, sau đó trả về nhánh. Đó là hợp nhất ff hoặc cam kết chỉ trên nhánh và không có cách nào tốt để tìm ra cam kết đúng.
  • Nếu cam kết và chi nhánh giống nhau, sau đó trả lại cam kết.

~/bin/git-find-merge kịch bản:

#!/bin/bash

commit=$1
if [ -z $commit ]; then
    echo 1>&2 "fatal: commit is required"
    exit 1
fi
commit=$(git rev-parse $commit)
branch=${2-@}

# if branch points to commit (both are same), then return commit
if [ $commit == $(git rev-parse $branch) ]; then
    git log -1 $commit
    exit
fi

# if commit is a merge commit on first-parent path of branch,
# then return commit
# if commit is a NON-merge commit on first-parent path of branch,
# then return branch as it's either a ff merge or commit is only on branch
# and there is not a good way to figure out the right commit
if [[ $(git log --first-parent --pretty='%P' $commit..$branch | \
    cut -d' ' -f1 | \
    grep $commit | wc -l) -eq 1 ]]; then
    if [ $(git show -s --format="%P" $commit | wc -w) -gt 1 ]; then
        # if commit is a merge commit
        git log -1 $commit
    else
        # if commit is a NON-merge commit
        echo 1>&2 ""
        echo 1>&2 "error: returning the branch commit (ff merge or commit only on branch)"
        echo 1>&2 ""
        git log -1 $branch
    fi
    exit
fi

# 1st common commit from bottom of first-parent and ancestry-path
merge=$(grep -f \
    <(git rev-list --first-parent  $commit..$branch) \
    <(git rev-list --ancestry-path $commit..$branch) \
        | tail -1)
if [ ! -z $merge ]; then
    git log -1 $merge
    exit
fi

# merge commit not found
echo 1>&2 "fatal: no merge commit found"
exit 1

Cho phép tôi làm điều này:

(master)
$ git find-merge <commit>    # to find when commit merged to current branch
$ git find-merge <branch>    # to find when branch merged to current branch
$ git find-merge <commit> pu # to find when commit merged to pu branch

Kịch bản này cũng có sẵn trên github của tôi .


2
git log --topo-order

Sau đó tìm kiếm sự hợp nhất đầu tiên trước khi cam kết.


1

Phiên bản ruby ​​của tôi về ý tưởng @ robinst, hoạt động nhanh hơn gấp đôi (điều này rất quan trọng khi tìm kiếm cam kết rất cũ).

find-commit.rb

commit = ARGV[0]
master = ARGV[1] || 'origin/master'

unless commit
  puts "Usage: find-commit.rb commit [master-branch]"
  puts "Will show commit that merged <commit> into <master-branch>"
  exit 1
end

parents = `git rev-list #{commit}..#{master} --reverse --first-parent --merges`.split("\n")
ancestry = `git rev-list #{commit}..#{master} --reverse --ancestry-path --merges`.split("\n")
merge = (parents & ancestry)[0]

if merge
  system "git show #{merge}"
else
  puts "#{master} doesn't include #{commit}"
  exit 2
end

Bạn chỉ có thể sử dụng nó như thế này:

ruby find-commit.rb SHA master

0

Bạn có thể thử một cái gì đó như thế này. Ý tưởng là lặp đi lặp lại qua tất cả các cam kết hợp nhất và xem liệu cam kết "c" có thể truy cập được từ một trong số chúng không:

$ git log --merges --format='%h' master | while read mergecommit; do
  if git log --format='%h' $mergecommit|grep -q $c; then
    echo $mergecommit;
    break
  fi
done

Điều này cũng giống như:git rev-list <SHA-1_for_c>..master --ancestry-path --merges
Eugen Konkov

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.