Tìm kiếm chi nhánh mà một cam kết Git đến từ


649

Có cách nào để tìm ra nhánh mà một cam kết đến từ giá trị băm SHA-1 của nó không?

Điểm thưởng nếu bạn có thể cho tôi biết cách thực hiện điều này bằng Ruby Grit.


4
Các phương pháp khác nhau dưới đây là thực dụng, hữu ích, cách để làm việc suy ra một có thể xảy ra câu trả lời, nhưng hãy lưu ý rằng trong git câu hỏi chính nó là một sự hiểu lầm, cam kết không đến từ các chi nhánh. Các chi nhánh đến và đi, họ di chuyển, chỉ cam kết đại diện cho lịch sử repo thực sự. Sau đó, một lần nữa, đây không phải là một cách để nói giải pháp dưới đây là xấu. Chỉ cần biết không ai trong số họ đưa ra một câu trả lời hoàn toàn đáng tin cậy, điều không thể đạt được bằng thiết kế trong git. (Một trường hợp đơn giản là các chi nhánh bị xóa: Tôi phân nhánh, tôi cam kết hai lần, tôi hợp nhất vào một chi nhánh khác, tôi xóa chi nhánh đầu tiên.
Cam

1
Tôi có một trường hợp tôi rõ ràng kéo một bản sao nông sâu 1 từ thẻ. Nó rẻ và dễ dàng và hiệu quả ... và cho đến bây giờ đã làm mọi thứ tôi muốn thật đẹp. Bây giờ tôi muốn biết thẻ đó được mở trên nhánh nào, tôi đã hos, ít nhất là cho chi tiết đó. Bạn không thể luôn về nhà, lol
Paul Hodges

Câu trả lời:


865

Mặc dù Dav chính xác là thông tin không được lưu trữ trực tiếp, nhưng điều đó không có nghĩa là bạn không thể tìm ra. Dưới đây là một vài điều bạn có thể làm.

Tìm chi nhánh cam kết là trên

git branch -a --contains <commit>

Điều này sẽ cho bạn biết tất cả các chi nhánh có cam kết nhất định trong lịch sử của họ. Rõ ràng điều này ít hữu ích hơn nếu các cam kết đã được hợp nhất.

Tìm kiếm các reflog

Nếu bạn đang làm việc trong kho lưu trữ mà cam kết được thực hiện, bạn có thể tìm kiếm các reflog cho dòng cho cam kết đó. Các reflog cũ hơn 90 ngày được git-gc cắt tỉa, vì vậy nếu cam kết quá cũ, bạn sẽ không tìm thấy nó. Điều đó nói rằng, bạn có thể làm điều này:

git reflog show --all | grep a871742

để tìm cam kết a871742. Lưu ý rằng bạn PHẢI sử dụng chữ viết tắt 7 chữ số đầu tiên của cam kết. Đầu ra phải giống như thế này:

a871742 refs/heads/completion@{0}: commit (amend): mpc-completion: total rewrite

chỉ ra rằng cam kết đã được thực hiện trên nhánh "hoàn thành". Đầu ra mặc định hiển thị băm cam kết viết tắt, vì vậy hãy chắc chắn không tìm kiếm băm đầy đủ nếu không bạn sẽ không tìm thấy gì.

git reflog showthực ra chỉ là một bí danh cho git log -g --abbrev-commit --pretty=oneline, vì vậy nếu bạn muốn tìm hiểu về định dạng đầu ra để làm cho những thứ khác nhau có sẵn cho grep cho, đó là điểm khởi đầu của bạn!

Nếu bạn không làm việc trong kho lưu trữ nơi cam kết được thực hiện, cách tốt nhất bạn có thể làm trong trường hợp này là kiểm tra các reflog và tìm thấy khi cam kết lần đầu tiên được đưa vào kho lưu trữ của bạn; với bất kỳ may mắn nào, bạn đã lấy được chi nhánh mà nó đã cam kết. Điều này phức tạp hơn một chút, bởi vì bạn không thể đi bộ cả cây cam kết và reflog đồng thời. Bạn muốn phân tích đầu ra reflog, kiểm tra từng hàm băm để xem nó có chứa cam kết mong muốn hay không.

Tìm một cam kết hợp nhất tiếp theo

Điều này phụ thuộc vào quy trình công việc, nhưng với quy trình công việc tốt, các cam kết được thực hiện trên các nhánh phát triển sau đó được hợp nhất. Bạn có thể làm điều này:

git log --merges <commit>..

để xem các cam kết hợp nhất có cam kết đã cho như một tổ tiên. (Nếu cam kết chỉ được hợp nhất một lần, thì lần đầu tiên sẽ là sự hợp nhất mà bạn theo sau; nếu không, bạn sẽ phải kiểm tra một vài, tôi cho rằng.

Nếu bạn muốn có thể tin tưởng vào việc này, bạn có thể muốn sử dụng --no-fftùy chọn git mergeđể buộc hợp nhất tạo cam kết ngay cả trong trường hợp chuyển tiếp nhanh. (Tuy nhiên, đừng quá háo hức. Điều đó có thể trở nên khó hiểu nếu bị lạm dụng.) Câu trả lời của VonC cho một câu hỏi liên quan được xây dựng một cách hữu ích về chủ đề này.


5
+1. Sự vắng mặt tuyệt đối của thông tin cho vấn đề cụ thể đó làm cho bạn tự hỏi nếu nó thực sự là một vấn đề? Chi nhánh có thể thay đổi, đổi tên hoặc bị xóa bất cứ lúc nào. Có thể git describelà đủ, cho các thẻ (chú thích) có thể được xem là quan trọng hơn so với các chi nhánh.
VonC

9
Tôi chắc chắn đồng ý - các chi nhánh có nghĩa là nhẹ và linh hoạt. Nếu bạn chấp nhận một quy trình công việc trong đó thông tin đó trở nên quan trọng, bạn có thể sử dụng --no-fftùy chọn để đảm bảo luôn có một cam kết hợp nhất, vì vậy bạn luôn có thể theo dõi đường dẫn của một cam kết nhất định khi nó được hợp nhất với chủ.
Cascabel

32
FYI, nếu bạn muốn tìm các cam kết chỉ tồn tại trên điều khiển từ xa, hãy thêm -acờ vào lệnh đầu tiên, ví dụ:git branch -a --contains <commit>
Ngày Jonathan

8
@JonathanDay: Không, điều đó sẽ tìm thấy các cam kết trên bất kỳ chi nhánh nào. Nếu bạn chỉ muốn những cái trên remote, hãy sử dụng -r.
Cascabel

7
@SimonTewsi Giống như tôi đã nói, nếu đó thực sự là một vấn đề, hãy sử dụng merge --no-ffđể ghi lại tên chi nhánh một cách đáng tin cậy khi bạn hợp nhất. Nhưng nếu không, hãy nghĩ tên chi nhánh là nhãn ngắn tạm thời và cam kết mô tả là tên vĩnh viễn. "Cái tên ngắn nào chúng ta đã đề cập đến trong quá trình phát triển?" thực sự không nên là một câu hỏi quan trọng như "cam kết này làm gì?"
Cascabel

154

Lệnh đơn giản này hoạt động như một bùa mê:

git name-rev <SHA>

Ví dụ: trong đó nhánh thử nghiệm là tên nhánh):

git name-rev 651ad3a
251ad3a remotes/origin/test-branch

Ngay cả điều này đang làm việc cho các kịch bản phức tạp, như:

origin/branchA/
              /branchB
                      /commit<SHA1>
                                   /commit<SHA2>

Ở đây git name-rev commit<SHA2>trả về nhánhB .


3
Bạn đã cứu ngày của tôi. Đây là giải pháp hoàn hảo.
Taco

7
Và chỉ mất 8 năm cho giải pháp hoàn hảo này. Cảm ơn!
S1J0

6
Tôi thấy git name-rev --name-only <SHA>hữu ích hơn khi chỉ lấy tên chi nhánh. Câu hỏi của tôi ... Nó có thể trả lại nhiều hơn một chi nhánh trong mọi trường hợp không?
Trưởng khoa Kayton

1
Đây phải là câu trả lời tốt nhất.
JCollier

1
Cảm ơn bạn @JCollier vì những lời tốt đẹp của bạn.
khichar.anil

47

Cập nhật tháng 12 năm 2013:

bình luận sschuberth

git-what-branch(Kịch bản Perl, xem bên dưới) dường như không còn được duy trì nữa. git-when-mergedlà một thay thế được viết bằng Python, nó hoạt động rất tốt đối với tôi.

Nó dựa trên " Tìm cam kết hợp nhất bao gồm một cam kết cụ thể ".

git when-merged [OPTIONS] COMMIT [BRANCH...]

Tìm khi một cam kết được sáp nhập vào một hoặc nhiều chi nhánh.
Tìm cam kết hợp nhất được đưa COMMITvào CHI NHÁNH (es) đã chỉ định.

Cụ thể, hãy tìm cam kết lâu đời nhất trong lịch sử cha mẹ đầu tiên BRANCHcó chứa COMMITtổ tiên.


Câu trả lời gốc tháng 9 năm 2010:

Sebastien Douche chỉ twitted (16 phút trước câu trả lời SO này):

git-what-Branch : Khám phá nhánh nào được cam kết hoặc cách nó đến nhánh được đặt tên

Đây là một kịch bản Perl từ Seth Robertson có vẻ rất thú vị:

TÓM TẮC

git-what-branch [--allref] [--all] [--topo-order | --date-order ]
[--quiet] [--reference-branch=branchname] [--reference=reference]
<commit-hash/tag>...

TỔNG QUAT

Hãy cho chúng tôi (theo mặc định) đường dẫn nhân quả và hợp nhất sớm nhất để thực hiện cam kết được yêu cầu đến một nhánh được đặt tên. Nếu một cam kết được thực hiện trực tiếp trên một nhánh có tên, đó rõ ràng là con đường sớm nhất.

Theo con đường nhân quả sớm nhất, chúng tôi có nghĩa là con đường hợp nhất thành một nhánh được đặt tên sớm nhất, theo thời gian cam kết (trừ khi --topo-orderđược chỉ định).

HIỆU SUẤT

Nếu nhiều nhánh (ví dụ hàng trăm) chứa cam kết, hệ thống có thể mất nhiều thời gian (đối với một cam kết cụ thể trong cây Linux, phải mất 8 giây để khám phá một nhánh, nhưng có hơn 200 nhánh ứng cử viên) để theo dõi đường dẫn đến từng cam kết.
Lựa chọn một cụ thể --reference-branch --reference tagđể kiểm tra sẽ nhanh hơn hàng trăm lần (nếu bạn có hàng trăm chi nhánh ứng cử viên).

VÍ DỤ

 # git-what-branch --all 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4
 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4 first merged onto master using the following minimal temporal path:
   v2.6.12-rc3-450-g1f9c381 merged up at v2.6.12-rc3-590-gbfd4bda (Thu May  5 08:59:37 2005)
   v2.6.12-rc3-590-gbfd4bda merged up at v2.6.12-rc3-461-g84e48b6 (Tue May  3 18:27:24 2005)
   v2.6.12-rc3-461-g84e48b6 is on master
   v2.6.12-rc3-461-g84e48b6 is on v2.6.12-n
   [...]

Chương trình này không tính đến các tác động của việc chọn cam kết quan tâm, chỉ hợp nhất các hoạt động.


4
git-what-branchdường như không được duy trì nữa. git-khi-sáp nhập là một thay thế được viết bằng Python hoạt động rất tốt đối với tôi.
sschuberth

@sschuberth cảm ơn bạn đã cập nhật. Tôi đã bao gồm nhận xét của bạn trong câu trả lời để dễ nhìn hơn.
VonC

37

Ví dụ: để tìm thấy c0118facam kết đó đến từ redesign_interactions:

* ccfd449 (HEAD -> develop) Require to return undef if no digits found
*   93dd5ff Merge pull request #4 from KES777/clean_api
|\
| * 39d82d1 Fix tc0118faests for debugging debugger internals
| * ed67179 Move &push_frame out of core
| * 2fd84b5 Do not lose info about call point
| * 3ab09a2 Improve debugger output: Show info about emitted events
| *   a435005 Merge branch 'redesign_interactions' into clean_api
| |\
| | * a06cc29 Code comments
| | * d5d6266 Remove copy/paste code
| | * c0118fa Allow command to choose how continue interaction
| | * 19cb534 Emit &interact event

Bạn nên chạy đi:

git log c0118fa..HEAD --ancestry-path --merges

Và cuộn xuống để tìm cam kết hợp nhất cuối cùng . Đó là:

commit a435005445a6752dfe788b8d994e155b3cd9778f
Merge: 0953cac a06cc29
Author: Eugen Konkov
Date:   Sat Oct 1 00:54:18 2016 +0300

    Merge branch 'redesign_interactions' into clean_api

Cập nhật

Hoặc chỉ một lệnh:

git log c0118fa..HEAD --ancestry-path --merges --oneline --color | tail -n 1

4
Đây là giải pháp duy nhất mang lại cho tôi kết quả mong muốn. Tôi đã thử phần còn lại.
crafter

Lưu ý điều này chỉ hoạt động nếu các bản tóm tắt cam kết hợp nhất chứa các tên nhánh được sử dụng trong các phép hợp nhất, theo mặc định chúng thường làm. Tuy nhiên git merge -m"Any String Here"sẽ che khuất thông tin chi nhánh nguồn và đích.
qneill

@qneill: Không, hợp nhất luôn có thông tin về các chi nhánh được hợp nhất : Merge: f6b70fa d58bdcb. Bạn có thể đặt tên cho cam kết hợp nhất của bạn như bạn với. Tôi sẽ không có vấn đề gì
Eugen Konkov

1
@EugenKonkov một khi các nhánh đã đi qua sự hợp nhất, lịch sử về đường dẫn nào trong biểu đồ mà chúng xuất phát được để lại trong reflog, nhưng không phải trong cơ sở dữ liệu đối tượng git. Tôi đã đăng một câu trả lời cố gắng giải thích những gì tôi đang nói
qneill

Phiên bản cập nhật hoạt động với tôi, lệnh đơn giản tìm thấy cam kết ban đầu
vpalmu

9

git branch --contains <ref>là lệnh "sứ" rõ ràng nhất để làm điều này. Nếu bạn muốn làm một cái gì đó tương tự chỉ với các lệnh "ống nước":

COMMIT=$(git rev-parse <ref>) # expands hash if needed
for BRANCH in $(git for-each-ref --format "%(refname)" refs/heads); do
  if $(git rev-list $BRANCH | fgrep -q $COMMIT); then
    echo $BRANCH
  fi
done

(crosspost từ câu trả lời SO này )


6

khichar.anil bao gồm hầu hết điều này trong câu trả lời của mình.

Tôi chỉ thêm cờ sẽ xóa các thẻ khỏi danh sách tên sửa đổi. Điều này cho chúng ta:

git name-rev --name-only --exclude=tags/* $SHA

Một cái gì đó dường như bị thiếu trong câu thứ hai.
Peter Mortensen

Tôi cập nhật mô tả. Thx cho các phản hồi.
phyatt

4

Tùy chọn của một người nghèo là sử dụng công cụ tig1 trên HEAD, tìm kiếm cam kết và sau đó theo dõi trực quan dòng từ cam kết đó sao lưu cho đến khi nhìn thấy một cam kết hợp nhất. Thông báo hợp nhất mặc định sẽ chỉ định nhánh nào sẽ được hợp nhất vào đâu :)

1 Tig là giao diện chế độ văn bản dựa trên ncurses cho Git. Nó hoạt động chủ yếu như một trình duyệt kho Git, nhưng nó cũng có thể hỗ trợ trong việc thay đổi các cam kết ở cấp độ khối và hoạt động như một máy nhắn tin cho đầu ra từ các lệnh Git khác nhau.


3

Như một thử nghiệm, tôi đã thực hiện một hook hook sau cam kết lưu trữ thông tin về nhánh hiện đang được kiểm tra trong siêu dữ liệu cam kết. Tôi cũng sửa đổi một chút gitk để hiển thị thông tin đó.

Bạn có thể kiểm tra nó ở đây: https://github.com/pajp/branch-info-commits


1
Nhưng nếu bạn muốn thêm siêu dữ liệu, tại sao không sử dụng ghi chú git, thay vì gây rối với các tiêu đề của cam kết? Xem stackoverflow.com/questions/5212957/ cấp , stackoverflow.com/questions/7298749/... hoặc stackoverflow.com/questions/7101158/...
VonC

Thành thật mà nói, tôi đã không biết ghi chú git tồn tại khi tôi viết nó. Vâng, sử dụng ghi chú git để đạt được điều tương tự có lẽ là một ý tưởng tốt hơn.
đồ lót

3

Tôi giải quyết vấn đề tương tự ( đường ống đa chức năng Jenkins ) - chỉ có thông tin cam kết và cố gắng tìm một tên chi nhánh nơi cam kết này ban đầu xuất phát. Nó phải làm việc cho các chi nhánh từ xa, bản sao địa phương không có sẵn.

Đây là những gì tôi làm việc với:

git rev-parse HEAD | xargs git name-rev

Tùy chọn bạn có thể tước đầu ra:

git rev-parse HEAD | xargs git name-rev | cut -d' ' -f2 | sed 's/remotes\/origin\///g'

0

Tôi nghĩ ai đó nên đối mặt với cùng một vấn đề không thể tìm ra chi nhánh, mặc dù nó thực sự tồn tại trong một chi nhánh.

Bạn nên kéo tất cả trước:

git pull --all

Sau đó thực hiện tìm kiếm chi nhánh:

git name-rev <SHA>

hoặc là:

git branch --contains <SHA>

0

Nếu OP đang cố gắng xác định lịch sử đã đi qua một nhánh khi một cam kết cụ thể được tạo ra ("tìm hiểu chi nhánh mà một cam kết đến từ giá trị băm SHA-1 của nó"), thì không có reflog thì không có các bản ghi trong cơ sở dữ liệu đối tượng Git cho thấy nhánh được đặt tên bị ràng buộc với lịch sử cam kết.

(Tôi đã đăng bài này dưới dạng câu trả lời để trả lời nhận xét.)

Hy vọng kịch bản này minh họa quan điểm của tôi:

rm -rf /tmp/r1 /tmp/r2; mkdir /tmp/r1; cd /tmp/r1
git init; git config user.name n; git config user.email e@x.io
git commit -m"empty" --allow-empty; git branch -m b1; git branch b2
git checkout b1; touch f1; git add f1; git commit -m"Add f1"
git checkout b2; touch f2; git add f2; git commit -m"Add f2"
git merge -m"merge branches" b1; git checkout b1; git merge b2
git clone /tmp/r1 /tmp/r2; cd /tmp/r2; git fetch origin b2:b2
set -x;
cd /tmp/r1; git log --oneline --graph --decorate; git reflog b1; git reflog b2;
cd /tmp/r2; git log --oneline --graph --decorate; git reflog b1; git reflog b2;

Đầu ra cho thấy thiếu bất kỳ cách nào để biết liệu cam kết với 'Thêm F1' đến từ nhánh b1 hoặc b2 từ bản sao từ xa / tmp / r2.

(Dòng cuối cùng của đầu ra ở đây)

+ cd /tmp/r1
+ git log --oneline --graph --decorate
*   f0c707d (HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: merge b2: Fast-forward
086c9ce b1@{1}: commit: Add f1
18feb84 b1@{2}: Branch: renamed refs/heads/master to refs/heads/b1
18feb84 b1@{3}: commit (initial): empty
+ git reflog b2
f0c707d b2@{0}: merge b1: Merge made by the 'recursive' strategy.
80c10e5 b2@{1}: commit: Add f2
18feb84 b2@{2}: branch: Created from b1
+ cd /tmp/r2
+ git log --oneline --graph --decorate
*   f0c707d (HEAD, origin/b2, origin/b1, origin/HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: clone: from /tmp/r1
+ git reflog b2
f0c707d b2@{0}: fetch origin b2:b2: storing head

Và đầu ra cho git log 80c10e5..HEAD --ancestry-path --merges --oneline --color | tail -n 1git log 086c9ce..HEAD --ancestry-path --merges --oneline --color | tail -n 1lệnh cho cả hai trường hợp là gì?
Eugen Konkov

Các SHA tất nhiên thay đổi bất cứ khi nào các lệnh được chạy, để bán lại các lệnh của bạn, tôi đã sử dụng ĐẦU ^ 1ĐẦU ^ 2 trong một lần chạy mới. Các lệnh và đầu ra là: $ git log HEAD^1..HEAD --ancestry-path --merges --oneline --color | tail -n 1sản lượng nào 376142d merge branches$ git log HEAD^2..HEAD --ancestry-path --merges --oneline --color | tail -n 1sản lượng nào 376142d merge branches - hiển thị tóm tắt cam kết hợp nhất, mà (như tôi đã khẳng định) có thể được ghi đè khi hợp nhất được tạo, có thể làm mờ lịch sử chi nhánh của hợp nhất.
qneill

0

TL; DR:

Sử dụng dưới đây nếu bạn quan tâm đến trạng thái thoát vỏ:

  • branch-current - tên của chi nhánh hiện tại
  • branch-names - tên chi nhánh sạch (một trên mỗi dòng)
  • branch-name - Đảm bảo rằng chỉ có một chi nhánh được trả về từ branch-names

Cả hai branch-namebranch-nameschấp nhận một cam kết làm đối số và mặc định HEADnếu không được đưa ra.


Bí danh hữu ích trong kịch bản

branch-current = "symbolic-ref --short HEAD"  # https://stackoverflow.com/a/19585361/5353461
branch-names = !"[ -z \"$1\" ] && git branch-current 2>/dev/null || git branch --format='%(refname:short)' --contains \"${1:-HEAD}\" #"  # https://stackoverflow.com/a/19585361/5353461
branch-name = !"br=$(git branch-names \"$1\") && case \"$br\" in *$'\\n'*) printf \"Multiple branches:\\n%s\" \"$br\">&2; exit 1;; esac; echo \"$br\" #"

Cam kết chỉ có thể truy cập từ chỉ một chi nhánh

% git branch-name eae13ea
master
% echo $?
0
  • Đầu ra là STDOUT
  • Giá trị thoát là 0.

Cam kết có thể truy cập từ nhiều chi nhánh

% git branch-name 4bc6188
Multiple branches:
attempt-extract
master%
% echo $?
1
  • Đầu ra là STDERR
  • Giá trị thoát là 1 .

Do trạng thái thoát, chúng có thể được xây dựng một cách an toàn. Ví dụ: để có được điều khiển từ xa được sử dụng để tìm nạp:

remote-fetch = !"branch=$(git branch-name \"$1\") && git config branch.\"$branch\".remote || echo origin #"

-1

Để tìm chi nhánh địa phương:

grep -lR YOUR_COMMIT .git/refs/heads | sed 's/.git\/refs\/heads\///g'

Để tìm chi nhánh từ xa:

grep -lR $commit .git/refs/remotes | sed 's/.git\/refs\/remotes\///g'

1
một số giải thích về các ví dụ của bạn sẽ rất tuyệt
Eugen Konkov

-4

Ngoài việc tìm kiếm trong tất cả các cây cho đến khi bạn tìm thấy một hàm băm phù hợp, không.


7
Tôi thực sự không muốn hạ thấp điều này, bởi vì nói đúng ra rằng git không lưu trữ thông tin đó vĩnh viễn, nhưng dù sao cũng rất có thể tìm ra. Xem câu trả lời của tôi.
Cascabel
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.