Git으로 지점을 찾으십니까?


454

지점 마스터와 A가 있고 둘 사이의 많은 병합 활동이있는 저장소가 있습니다. master를 기반으로 지점 A를 만들 때 저장소에서 커밋을 어떻게 찾을 수 있습니까?

내 저장소는 기본적으로 다음과 같습니다.

-- X -- A -- B -- C -- D -- F  (master) 
          \     /   \     /
           \   /     \   /
             G -- H -- I -- J  (branch A)

나는 개정판 A를 찾고 있는데, 이것이 git merge-base (--all)발견 되지 않습니다 .


답변:


498

나는 똑같은 것을 찾고 있었고이 질문을 찾았습니다. 물어 주셔서 감사합니다!

그러나, 나는 내가 여기에있는 답변을하지 않는 것 발견 확실히 당신이 (또는 내가 찾던 것을) 물어 대답을 - 그들은 줄 것 G(가) 대신에, 커밋 A커밋.

그래서 나는 다음과 같은 트리 (시간순으로 지정된 문자)를 만들었으므로 테스트 할 수 있습니다.

A - B - D - F - G   <- "master" branch (at G)
     \   \     /
      C - E --'     <- "topic" branch (still at E)

나는 (당신의 것이 아니라이 그래프를 참조하십시오) B를 얻었지만 A (D 또는 E는 아님)를 얻지 않기를 원했기 때문에 이것은 당신과 약간 다르게 보입니다. 다음은 SHA 접두사와 커밋 메시지에 첨부 된 문자입니다 ( 누군가에게 흥미가 있다면 내 repo를 여기서 복제 할 수 있습니다 ).

G: a9546a2 merge from topic back to master
F: e7c863d commit on master after master was merged to topic
E: 648ca35 merging master onto topic
D: 37ad159 post-branch commit on master
C: 132ee2a first commit on topic branch
B: 6aafd7f second commit on master before branching
A: 4112403 initial commit on master

그래서, 목표는 B를 찾을 수 있습니다 . 약간의 땜질 후 내가 찾은 세 가지 방법은 다음과 같습니다.


1. gitk를 사용하여 시각적으로 :

마스터에서 볼 때 이와 같은 트리가 시각적으로 표시되어야합니다.

마스터에서 gitk 화면 캡처

또는 여기 (주제에서 본) :

주제에서 gitk 화면 캡처

두 경우 모두 B내 그래프에 있는 커밋을 선택했습니다 . 클릭하면 전체 SHA가 그래프 바로 아래 텍스트 입력 필드에 표시됩니다.


2. 시각적으로, 그러나 터미널에서 :

git log --graph --oneline --all

(편집 / 사이드 노트 : 추가 --decorate도 흥미로울 수 있습니다. 브랜치 이름, 태그 등의 표시를 추가합니다. 아래 출력에 사용이 반영되지 않으므로 위의 명령 줄에 추가하지 마십시오.)

어떤 (가정 git config --global color.ui auto) 을 보여줍니다 :

자식 로그의 출력 --graph --oneline --all

또는 직선 텍스트로 :

* a9546a2 주제에서 마스터로 다시 병합
| \  
| * 주제에 마스터를 병합하는 648ca35
| | \  
| * | 132ee2a 토픽 브랜치에서 첫 커밋
* | | 마스터가 주제에 병합 된 후 마스터에서 e7c863d 커미트
| | /  
| / |   
* | 마스터에서 37ad159 지점 후 커밋
| /  
* 분기 전에 마스터에서 6aafd7f 초 커밋
마스터에서 4112403 초기 커밋

두 경우 모두 6aafd7f 커밋이 가장 낮은 공통점, 즉 B내 그래프 또는 A귀하의 것으로 간주됩니다 .


3. 포탄 마술로 :

위와 같은 것을 원했는지 또는 하나의 수정본을 가져 오는 단일 명령과 다른 것을 요구하지 않는지 질문에 지정하지 않았습니다. 자, 여기에 후자가 있습니다 :

diff -u <(git rev-list --first-parent topic) \
             <(git rev-list --first-parent master) | \
     sed -ne 's/^ //p' | head -1
6aafd7ff98017c816033df18395c5c1e7829960d

~ / .gitconfig에 넣을 수도 있습니다 (참고 : 후행 대시가 중요합니다. 브라이언 에게 관심을 가져 주셔서 감사 합니다) .

[alias]
    oldest-ancestor = !zsh -c 'diff -u <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne \"s/^ //p\" | head -1' -

다음과 같은 명령 줄을 통해 수행 할 수 있습니다.

git config --global alias.oldest-ancestor '!zsh -c '\''diff -u <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne "s/^ //p" | head -1'\'' -'

참고 : zsh그냥 간단하게 수 있었다 bash, 그러나 sh의지 하지 작업 - <()구문은 바닐라에 존재하지 않습니다 sh. (이 페이지의 다른 답변에 대한 의견으로 알려 주셔서 @conny 감사합니다!)

참고 : 위의 대체 버전 :

감사합니다 liori 에 대한 지적 즉, (동일 지점을 비교할 때 위에서 아래로 떨어질 수 있다는 것을, 그리고 믹스에서 나오지 양식을 제거하는 대체 DIFF 양식을오고, 이것은 "안전"이 (즉,이 결과를 반환한다 가장 최근의 커밋)을 마스터와 마스터를 비교하더라도) :

.git-config 라인으로 :

[alias]
    oldest-ancestor = !zsh -c 'diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | head -1' -

껍질에서 :

git config --global alias.oldest-ancestor '!zsh -c '\''diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | head -1'\'' -'

따라서 내 테스트 트리 (한동안 사용할 수 없었습니다. 죄송합니다. 돌아 왔습니다)에서 이제 마스터와 주제 모두에서 작동합니다 (각각 G와 B를 커밋합니다). 다른 형태에 대해 다시 한 번 감사드립니다.


그래서 그것이 제가 생각한 것입니다. 그것은 나를 위해 일하는 것 같습니다. 또한 다음과 같이 유용한 몇 가지 별칭을 추가 할 수 있습니다.

git config --global alias.branchdiff '!sh -c "git diff `git oldest-ancestor`.."'
git config --global alias.branchlog '!sh -c "git log `git oldest-ancestor`.."'

행복한 git-ing!


5
lindes에게 감사합니다. 쉘 옵션은 장기 실행 유지 보수 분기의 분기점을 찾고자하는 상황에 적합합니다. 과거에 수천 번 커밋 될 수있는 개정을 찾고있을 때 시각적 옵션은 실제로 수정하지 않을 것입니다. * 8 ')
Mark Booth

4
세 번째 방법에서는 컨텍스트에 변경되지 않은 첫 번째 줄이 표시된다는 것에 의존합니다. 이것은 특정 경우에 발생하지 않거나 약간 다른 요구 사항이있는 경우에는 발생하지 않습니다 (예 : 역사 중 하나만 --first-parent가 필요하며 때로는이를 사용하는 스크립트 에서이 방법을 사용하고 있습니다) 양쪽에 가지). 나는 diffif-then-else 모드를 사용하고 충분히 큰 컨텍스트를 갖는 대신 출력에서 ​​변경 / 삭제 된 행을 지우는 것이 더 안전하다는 것을 알았습니다 diff --old-line-format='' --new-line-format='' <(git rev-list …) <(git rev-list …)|head -1.
liori

3
git log -n1 --format=format:%H $(git log --reverse --format=format:%H master..topic | head -1)~생각합니다
Tobias Schulte

4
@ JakubNarębski : 이 트리에 git merge-base --fork-point ...커밋 B (커밋 6aafd7f) 를 줄 수있는 방법 이 있습니까? 나는 당신이 그것을 게시했을 때 다른 것들로 바빴고, 좋은 소리로 들렸지 만 마침내 시도했지만 작동하지 않습니다 ... 더 최근에 무언가를 얻거나 조용한 실패 (오류 없음) 메시지,하지만 종료 상태 1), 같은 인수를 시도 git co master; git merge-base --fork-point topic, git co topic; git merge-base --fork-point master, git merge-base --fork-point topic master중 하나를 체크 아웃에 대한 (), 등이있다 뭔가 내가 잘못을하고 또는 누락되어 있습니까?
lindes

25
@ JakubNarębski @lindes --fork-point는 참조 로그 를 기반으로하므로 로컬에서 변경 한 경우에만 작동합니다. 그럼에도 불구하고 reflog 항목은 만료되었을 수 있습니다. 유용하지만 전혀 신뢰할 수는 없습니다.
Daniel Lubarov 2016 년

131

당신은 찾고 있습니다 git merge-base:

git merge-base 는 3 방향 병합에서 사용할 두 커밋 사이에서 가장 일반적인 공통 조상을 찾습니다. 후자가 전자의 조상이라면 하나의 공통 조상이 다른 공통 조상보다 습니다. 더 나은 공통 조상이없는 공통 조상은 최고의 공통 조상 , 즉 병합 기반 입니다. 한 쌍의 커밋에 대해 둘 이상의 병합 기반이있을 수 있습니다.


10
또한 주 --all"에 옵션 git merge-base"
야쿱 Narębski

10
이것은 원래의 질문에 대한 답은 아니지만 대부분의 사람들은 이것이 가장 간단한 질문입니다. :)
FelipeC

6
그는 자식 병합베이스의 결과 wan't하지 않았다
톰 태너

9
@TomTanner : 방금 질문 내역을 보았고 git merge-base답변이 게시 된 후 약 5 시간 후에 해당 답변을 포함하도록 원래 질문이 편집되었습니다 . 그럼에도 불구하고 나는이 답변을 그대로 두겠습니다. 검색을 통해이 질문을 찾는 다른 사람에게는 여전히 유용 할 수 있기 때문입니다.
Greg Hewgill

나는 당신이 merge-base의 결과를 사용할 수 있다고 생각했다-그리고 나서 merge-base --is-ancestor를 사용하여 리턴 커밋 목록을 조사하여 목록에서 가장 오래된 공통 조상을 찾을 수 있다고 생각하지만 이것이 가장 간단한 방법은 아닙니다. .
Matt_G

42

나는 git rev-list이런 종류의 것을 사용했습니다. 예를 들어 ( 3 개의 점에 유의하십시오 )

$ git rev-list --boundary branch-a...master | grep "^-" | cut -c2-

분기점을 뱉어냅니다. 이제는 완벽하지 않습니다. 마스터를 브랜치 A에 두 번 병합했기 때문에 가능한가지 브랜치 포인트 (기본적으로 원래 브랜치 포인트와 마스터를 브랜치 A를 병합 한 각 포인트)가 분할됩니다. 그러나 최소한 가능성을 좁혀 야합니다.

다음과 ~/.gitconfig같이 해당 명령을 별칭에 추가 했습니다.

[alias]
    diverges = !sh -c 'git rev-list --boundary $1...$2 | grep "^-" | cut -c2-'

그래서 나는 그것을 다음과 같이 부를 수 있습니다.

$ git diverges branch-a master

참고 : 이것은 공통 조상이 아닌 지점에서 첫 번째 커밋을주는 것으로 보입니다. (즉 , 원래 질문의 그래프에 따라 G대신에 제공 됩니다 A.) A현재 답변을 얻습니다 .
lindes

@lindes : 시도한 모든 경우에 공통 조상을 제공합니다. 그렇지 않은 예가 있습니까?
mipadi

예. 에 내 대답은 (당신이 복제 할 수있는 REPO에 대한 링크를 가지고, git checkout topic다음에 이것을 실행하는 topic대신에 branch-a), 그 목록 648ca357b946939654da12aaf2dc072763f3caee37ad15952db5c065679d7fc31838369712f0b338- 모두 37ad159648ca35(후자의 현재 HEAD되는 현재 지점의 ancestory에 topic) 그러나 분기가 발생하기 전의 시점도 아닙니다. 다른 것을 얻습니까?
lindes

@lindes : 리포지토리를 복제하지 못했습니다 (사용 권한 문제 일 수 있습니다).
mipadi

어머 미안합니다! 알려 주셔서 감사합니다. git update-server-info를 실행하는 것을 잊었습니다. 지금 가면 좋을 것입니다. :)
lindes

31

간결한 명령을 좋아한다면

git rev-list $ (git rev-list --first-parent ^ branch_name master | tail -n1) ^^! 

여기에 설명이 있습니다.

다음 명령은 branch_name이 작성된 후 발생한 마스터의 모든 커밋 목록을 제공합니다.

git rev-list --first-parent ^ branch_name 마스터 

가장 빠른 커밋에만 관심이 있기 때문에 출력의 마지막 줄을 원합니다.

git rev-list ^ branch_name-처음 부모 마스터 | 꼬리 -n1

초기의 부모는 "이 branch_name"의 조상이 아니다, 정의, 커밋 는 뭔가의 조상이기 때문에 "branch_name,"와 "마스터"에 "마스터." 따라서 두 지점 모두에서 가장 빠른 커밋이 있습니다.

명령

git rev-list commit ^^!

부모 커밋 참조를 표시하는 방법 일뿐입니다. 당신은 사용할 수 있습니다

자식 로그 -1 커밋 ^

또는 무엇이든.

추신 : 저는 조상 질서와 관련이 없다는 주장에 동의하지 않습니다. 그것은 당신이 원하는 것에 달려 있습니다. 예를 들어이 경우

_C1___C2_______ 마스터
  \ \ _XXXXX_ 분기 A (X는 마스터와 A 사이의 임의의 교차를 나타냄)
   \ _____ / 지점 B

C2를 "분기"커밋으로 출력하는 것이 완벽합니다. 개발자가 "마스터"에서 분기했을 때입니다. 그가 지점을 만들었을 때 지점 "B"는 지점에 병합되지 않았습니다! 이것이이 게시물의 솔루션이 제공하는 것입니다.

분기 "A"의 원점에서 마지막 커밋까지의 모든 경로가 C를 통과하도록 마지막 커밋 C가 원하는 경우 상위 순서를 무시하려고합니다. 그것은 순전히 토폴로지이며 동시에 두 가지 버전의 코드가있을 때부터 아이디어를 제공합니다. 그때는 병합 기반 기반 접근 방식을 사용하고 있으며 내 예에서는 C1을 반환합니다.


3
이것은 가장 깨끗한 답변입니다.이 표를 맨 위로 투표합시다. 제안 된 편집은 : git rev-list commit^^!간단하게 할 수 있습니다git rev-parse commit^
러셀 데이비스

이것이 답이되어야합니다!
trinth 2016 년

2
이 답변은 그냥 좋은 교체 git rev-list --first-parent ^branch_name mastergit rev-list --first-parent branch_name ^master마스터 분기 0 커밋 앞서 다른 지점 (그것에 빠른 전송 가능)의, 아니 출력이 생성되지한다면 때문이다. 내 솔루션을 사용하면 master가 엄격하게 앞서면 (즉, 지점이 완전히 병합 된 경우) 출력이 생성되지 않습니다.
Michael Schmeißer

3
내가 완전히 누락 된 경우가 아니면 작동하지 않습니다. 예제 브랜치에는 양방향으로 병합됩니다. 당신이 그것을 고려한 것처럼 들리지만, 이것이 당신의 대답을 실패하게 할 것이라고 믿습니다. git rev-list --first-parent ^topic master단지 당신이 처음에서 마지막 병합 후 커밋에 다시 걸릴 mastertopic(즉,도 존재하는 경우).
jerry

1
@jerry 당신은 맞습니다,이 대답은 쓰레기입니다; 예를 들어, backmerge가 방금 발생하고 (마스터를 주제로 병합) master에 새로운 커밋이없는 경우 첫 번째 git rev-list --first-parent 명령은 아무것도 출력하지 않습니다.
TamaMcGlinn

19

이 스레드의 많은 답변이 질문에 대한 답변을 제공하지 않기 때문에 각 솔루션의 결과에 대한 요약과 질문에 주어진 저장소를 복제하는 데 사용한 스크립트가 있습니다.

일지

주어진 구조로 저장소를 만들면 git log of :

$ git --no-pager log --graph --oneline --all --decorate
* b80b645 (HEAD, branch_A) J - Work in branch_A branch
| *   3bd4054 (master) F - Merge branch_A into branch master
| |\  
| |/  
|/|   
* |   a06711b I - Merge master into branch_A
|\ \  
* | | bcad6a3 H - Work in branch_A
| | * b46632a D - Work in branch master
| |/  
| *   413851d C - Merge branch_A into branch master
| |\  
| |/  
|/|   
* | 6e343aa G - Work in branch_A
| * 89655bb B - Work in branch master
|/  
* 74c6405 (tag: branch_A_tag) A - Work in branch master
* 7a1c939 X - Work in branch master

내 유일한 추가 사항은, 우리가 브랜치를 만든 지점과 따라서 찾고자하는 커밋에 대해 명시 적으로 만드는 태그입니다.

작동하는 솔루션

작동하는 유일한 솔루션이 제공하는 하나입니다 lindes 제대로 반환 A:

$ diff -u <(git rev-list --first-parent branch_A) \
          <(git rev-list --first-parent master) | \
      sed -ne 's/^ //p' | head -1
74c6405d17e319bd0c07c690ed876d65d89618d5

으로 찰스 베일리는 하지만 지적이 솔루션은 매우 취성이다.

당신이 경우 branch_Amaster다음 병합 masterbranch_A커밋 개입하지 않고 다음 lindes '솔루션은 당신에게주는 가장 최근의 첫번째 divergance을 .

즉, 워크 플로에서 장기 실행 브랜치의 브랜치 지점에 태그를 지정해야한다고 생각합니다. 나중에 브랜치를 확실하게 찾을 수 있다고 보장 할 수 없기 때문입니다.

에 이건 정말 모든 종기 git의 어떤 부족 hg호출 라는 이름의 지점을 . 블로거 jhw는 그의 계보 vs. 가족 을 그의 기사가 왜 내가 왜냐면 왜 Mercurial보다 Git보다 좋아 하는가 와 그의 후속 기사 인 More On Mercurial vs. Git (그래프 포함!)라고한다 . 나는 사람들이 어떤 수은 개종자가 한 금주의 추천 이유는 그들을보고 읽는 것이 좋습니다 가지 이름 으로 git.

작동하지 않는 솔루션

제공하는 솔루션 mipadi은 이 개 답변을 반환 I하고 C:

$ git rev-list --boundary branch_A...master | grep ^- | cut -c2-
a06711b55cf7275e8c3c843748daaa0aa75aef54
413851dfecab2718a3692a4bba13b50b81e36afc

에 의해 제공되는 용액 그렉 Hewgill의I

$ git merge-base master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54
$ git merge-base --all master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54

Karl이 제공하는 솔루션 은 X다음을 반환합니다 .

$ diff -u <(git log --pretty=oneline branch_A) \
          <(git log --pretty=oneline master) | \
       tail -1 | cut -c 2-42
7a1c939ec325515acfccb79040b2e4e1c3e7bbe5

스크립트

mkdir $1
cd $1
git init
git commit --allow-empty -m "X - Work in branch master"
git commit --allow-empty -m "A - Work in branch master"
git branch branch_A
git tag branch_A_tag     -m "Tag branch point of branch_A"
git commit --allow-empty -m "B - Work in branch master"
git checkout branch_A
git commit --allow-empty -m "G - Work in branch_A"
git checkout master
git merge branch_A       -m "C - Merge branch_A into branch master"
git checkout branch_A
git commit --allow-empty -m "H - Work in branch_A"
git merge master         -m "I - Merge master into branch_A"
git checkout master
git commit --allow-empty -m "D - Work in branch master"
git merge branch_A       -m "F - Merge branch_A into branch master"
git checkout branch_A
git commit --allow-empty -m "J - Work in branch_A branch"

git 버전이 이것과 많은 차이가 있는지 의심하지만,

$ git --version
git version 1.7.1

예제 저장소를 스크립팅하는 더 간단한 방법을 보여준 Charles Bailey 에게 감사 합니다.


2
Karl의 솔루션은 쉽게 고칠 수 diff -u <(git rev-list branch_A) <(git rev-list master) | tail -2 | head -1있습니다. repo를 만드는 지침을 제공해 주셔서 감사합니다 :)
FelipeC

"Karl이 제공 한 솔루션의 정리 된 변형은 X를 반환합니다"를 의미한다고 생각합니다. 원래 잘 작동했다 그것은 추악한 :-)
Karl

아니요, 원본이 제대로 작동하지 않습니다. 물론, 변형은 최악의 경우에도 작동합니다. 그러나 --topo-order 옵션을 추가하면 버전이 작동합니다. :)
FelipeC

@felipec- Charles Bailey 의 답변에 대한 나의 마지막 의견을 참조하십시오 . 아아 우리 채팅 (따라서 모든 오래된 주석)이 이제 삭제되었습니다. 시간이되면 답변을 업데이트하려고합니다.
Mark Booth

흥미 롭군 토폴로지가 기본값이라고 가정했습니다. 바보 나 :-)
Karl

10

일반적으로 불가능합니다. 브랜치 히스토리에서 명명 된 브랜치가 브랜치 오프되기 전에 브랜치 앤 병합은 명명 된 두 브랜치의 중간 브랜치가 동일하게 보입니다.

자식에서 브랜치는 히스토리 섹션 팁의 현재 이름입니다. 그들은 실제로 강한 정체성을 가지고 있지 않습니다.

두 커밋에 대한 병합 기반 (Greg Hewgill의 답변 참조)이 일반적으로 훨씬 유용하여 두 분기가 공유 한 가장 최근의 커밋을 제공하므로 이는 일반적으로 큰 문제가 아닙니다.

커밋의 부모 순서에 의존하는 솔루션은 브랜치 히스토리의 어느 시점에서 브랜치가 완전히 통합 된 상황에서는 분명히 작동하지 않습니다.

git commit --allow-empty -m root # actual branch commit
git checkout -b branch_A
git commit --allow-empty -m  "branch_A commit"
git checkout master
git commit --allow-empty -m "More work on master"
git merge -m "Merge branch_A into master" branch_A # identified as branch point
git checkout branch_A
git merge --ff-only master
git commit --allow-empty -m "More work on branch_A"
git checkout master
git commit --allow-empty -m "More work on master"

이 기법은 부모를 반대로하여 통합 병합을 수행 한 경우에도 적용됩니다 (예 : 임시 분기를 사용하여 마스터로 테스트 병합을 수행 한 다음 기능 분기로 빨리 전달하여 추가 빌드).

git commit --allow-empty -m root # actual branch point
git checkout -b branch_A
git commit --allow-empty -m  "branch_A commit"
git checkout master
git commit --allow-empty -m "More work on master"
git merge -m "Merge branch_A into master" branch_A # identified as branch point
git checkout branch_A
git commit --allow-empty -m "More work on branch_A"

git checkout -b tmp-branch master
git merge -m "Merge branch_A into tmp-branch (master copy)" branch_A
git checkout branch_A
git merge --ff-only tmp-branch
git branch -d tmp-branch

git checkout master
git commit --allow-empty -m "More work on master"


3
감사합니다 찰스, 당신은 내가 지점이 원래 분기 된 지점을 알고 싶다면 태그를 지정해야한다고 저를 설득 했습니다. 내가 정말 원하는 git것이 동등한했다 hg가 오래 살았 유지 보수 지점을 관리 할 것, '가지 이름들 때문에 훨씬 쉽다.
Mark Booth

1
"git에서 브랜치는 역사 섹션의 팁에 대한 현재 이름 일뿐입니다. 그들은 실제로 강력한 정체성을 가지고 있지 않습니다."그것은 말할 것도없고 Git 브랜치를 더 잘 이해해야한다는 것을 확신시켜주었습니다. 1)
dumbledad

브랜치 히스토리에서 명명 된 브랜치가 브랜치 오프되기 전에 브랜치 앤 병합은 명명 된 두 브랜치의 중간 브랜치가 동일하게 보입니다. 네. +1.
user1071847

5

어때요?

git log --pretty=oneline master > 1
git log --pretty=oneline branch_A > 2

git rev-parse `diff 1 2 | tail -1 | cut -c 3-42`^

작동합니다. 정말 번거롭지 만 실제로 찾은 것은 실제로 일을하는 것 같습니다.
GaryO

1
Git alias equivalent : diverges = !bash -c 'git rev-parse $(diff <(git log --pretty=oneline ${1}) <(git log --pretty=oneline ${2}) | tail -1 | cut -c 3-42)^'(임시 파일 없음)
conny

@conny : 오, 와우-<(foo) 구문을 본 적이 없습니다 ... 정말 유용합니다. 감사합니다! (FYI에서도 zsh에서 작동)
lindes

이것은 나에게 일반적인 조상이 아닌 지점에서 첫 번째 커밋을주는 것으로 보입니다. (즉 , 원래 질문의 그래프에 따라 G대신에 제공 됩니다 A.) 나는 대답을 찾았지만 현재 게시 할 것이라고 생각합니다.
lindes

'git log --pretty = oneline'대신 'git rev-list'를 수행 한 다음 컷을 건너 뛸 수도 있습니다. 또한 상위 지점의 분기점을 커밋하므로 tail -2 | head 1. So :diff -u <(git rev-list branch_A) <(git rev-list master) | tail -2 | head -1
FelipeC

5

분명히 나는 ​​뭔가를 잃어 버렸지 만 IMO는 위의 모든 문제가 발생합니다. 왜냐하면 우리는 항상 역사에서 되돌아 오는 분기 지점을 찾으려고 노력하고 있으며 가능한 병합 조합으로 인해 모든 종류의 문제가 발생합니다.

대신 두 지점이 많은 역사를 공유한다는 사실에 근거하여 다른 접근법을 따랐습니다. 분지 전의 모든 역사는 100 % 동일합니다. 따라서 되돌아가는 대신 내 제안은 앞으로 나아갈 것입니다. 커밋), 두 가지의 첫 번째 차이점을 찾습니다. 지점은 단순히 첫 번째 차이점의 부모가됩니다.

실제로:

#!/bin/bash
diff <( git rev-list "${1:-master}" --reverse --topo-order ) \
     <( git rev-list "${2:-HEAD}" --reverse --topo-order) \
--unified=1 | sed -ne 's/^ //p' | head -1

그리고 그것은 나의 모든 일반적인 경우를 해결하고 있습니다. 확실하지 않지만 국경이 있지만 ... ciao :-)


diff <(git rev-list "$ {1 : -master}"--first-parent) <(git rev-list "$ {2 : -HEAD}"--first-parent) -U1 | 꼬리 -1
알렉산더 버드

나는 이것이 더 빠르다는 것을 발견했다 (2-100x) :comm --nocheck-order -1 -2 <(git rev-list --reverse --topo-order topic) <(git rev-list --reverse --topo-order master) | head -1
Andrei Neculau


4

많은 연구와 토론을 거친 후에는 적어도 현재 버전의 Git에서는 모든 상황에서 작동하는 마법의 총알이 없다는 것이 분명합니다.

그렇기 때문에 tail브랜치 개념을 추가하는 몇 가지 패치를 작성했습니다 . 분기가 생성 될 때마다 원래 지점에 대한 포인터도 생성됩니다 tail. 이 참조는 지점이 리베이스 될 때마다 업데이트됩니다.

개발 분기의 분기점을 찾으려면을 사용 devel@{tail}하면됩니다.

https://github.com/felipec/git/commits/fc/tail


1
안정적인 솔루션 일 수 있습니다. 이것이 git에 들어갈 수 있는지 보았습니까? 풀 요청을 보지 못했습니다.
Alexander Klimetschek

@AlexanderKlimetschek 패치를 보내지 않았으며 패치가 허용되지 않을 것이라고 생각합니다. 그러나 나는 다른 방법을 시도했다 : "update-branch"훅은 매우 유사한 것을한다. 이 방법은 기본적으로 Git은 아무것도하지 않지만 후크를 사용하여 테일 브랜치를 업데이트 할 수 있습니다. 하지만 devel @ {tail}은 없지만 tails / devel을 대신 사용하는 것은 나쁘지 않습니다.
FelipeC

3

다음은 이전 답변 이전 답변 의 향상된 버전입니다 . 병합의 커밋 메시지에 따라 분기가 처음 작성된 위치를 찾습니다.

여기에 언급 된 모든 리포지토리에서 작동 하며 메일 링리스트에서 생성 된 까다로운 것들도 해결했습니다 . 나는 또한 이것에 대한 테스트썼다 .

find_merge ()
{
    local selection extra
    test "$2" && extra=" into $2"
    git rev-list --min-parents=2 --grep="Merge branch '$1'$extra" --topo-order ${3:---all} | tail -1
}

branch_point ()
{
    local first_merge second_merge merge
    first_merge=$(find_merge $1 "" "$1 $2")
    second_merge=$(find_merge $2 $1 $first_merge)
    merge=${second_merge:-$first_merge}

    if [ "$merge" ]; then
        git merge-base $merge^1 $merge^2
    else
        git merge-base $1 $2
    fi
}

3

다음 명령은 커밋 A의 SHA1을 나타냅니다.

git merge-base --fork-point A


부모 및 자식 분기가 서로 중간에 병합 된 경우에는 그렇지 않습니다.
nawfal

2

나는 기쁨을 얻는 것 같습니다

git rev-list branch...master

마지막 줄은 지점의 첫 번째 커밋이므로 부모를 얻는 것입니다. 그래서

git rev-list -1 `git rev-list branch...master | tail -1`^

나를 위해 일하는 것 같고 diffs 등이 필요하지 않습니다 (이 버전의 diff가 없으므로 도움이됩니다)

수정 : 마스터 지점에있는 경우 작동하지 않지만 스크립트 에서이 작업을 수행하므로 문제가 적습니다.


2

분기점에서 커밋을 찾으려면 이것을 사용할 수 있습니다.

git log --ancestry-path master..topicbranch

이 명령은 주어진 exmple에서 작동하지 않습니다. 커밋 범위에 대한 매개 변수로 무엇을 제공 하시겠습니까?
Jesper Rønn-Jensen

2

때로는 실제로 불가능하며 (추가 데이터가있는 경우가있는 경우를 제외하고) 여기에서 해결책이 작동하지 않습니다.

힘내는 참조 기록 (지점 포함)을 유지하지 않습니다. 각 분기 (헤드)의 현재 위치 만 저장합니다. 이것은 시간이 지남에 따라 git에서 일부 분기 기록을 잃을 수 있음을 의미합니다. 예를 들어 지점을 만들 때마다 어느 지점이 원래 지점인지 즉시 손실됩니다. 모든 지점은 다음과 같습니다.

git checkout branch1    # refs/branch1 -> commit1
git checkout -b branch2 # branch2 -> commit1

첫 번째 커밋이 브랜치라고 가정 할 수 있습니다. 이것은 사실이지만 항상 그런 것은 아닙니다. 위의 작업 후에 먼저 분기 중 하나에 커밋하는 것을 막을 것은 없습니다. 또한 git 타임 스탬프가 신뢰할 수 있다고 보장하지는 않습니다. 당신이 진정으로 구조적으로 가지가되기로 약속하기 전까지는 아닙니다.

다이어그램에서는 커밋을 개념적으로 지정하는 경향이 있지만 커밋 트리가 분기 될 때 git에는 안정적인 시퀀스 개념이 없습니다. 이 경우 숫자 (표시 순서)가 타임 스탬프에 의해 결정된다고 가정 할 수 있습니다 (모든 타임 스탬프를 동일하게 설정할 때 git UI가 작업을 처리하는 방법을 보는 것이 재미있을 수 있습니다).

이것은 인간이 개념적으로 기대하는 것입니다.

After branch:
       C1 (B1)
      /
    -
      \
       C1 (B2)
After first commit:
       C1 (B1)
      /
    - 
      \
       C1 - C2 (B2)

이것이 실제로 얻는 것입니다.

After branch:
    - C1 (B1) (B2)
After first commit (human):
    - C1 (B1)
        \
         C2 (B2)
After first commit (real):
    - C1 (B1) - C2 (B2)

B1을 원래 브랜치로 가정하지만 실제로는 단순히 죽은 브랜치 일 수 있습니다 (누군가 -b를 체크 아웃했지만 결코 커밋하지 않았습니다). git 내에서 합법적 인 브랜치 구조를 얻기 위해 커밋 할 때까지는 아닙니다.

Either:
      / - C2 (B1)
    -- C1
      \ - C3 (B2)
Or:
      / - C3 (B1)
    -- C1
      \ - C2 (B2)

C1은 C2와 C3보다 먼저 왔음을 항상 알고 있지만 C2가 C3보다 먼저 왔거나 C3이 C2보다 먼저 왔는지 확실하게 알 수는 없습니다 (예를 들어 워크 스테이션에서 시간을 설정할 수 있기 때문). 어느 지점이 먼저 왔는지 알 수 없으므로 B1과 B2도 오해의 소지가 있습니다. 많은 경우에 아주 좋고 일반적으로 정확한 추측을 할 수 있습니다. 레이스 트랙과 비슷합니다. 모든 것이 일반적으로 자동차와 동일하면 무릎 뒤에서 오는 자동차가 무릎 뒤에서 시작했다고 가정 할 수 있습니다. 우리는 또한 매우 신뢰할만한 협약을 가지고 있습니다. 예를 들어, 마스터는 거의 항상 가장 오래 살았던 지점을 대표하지만 슬프게도 이것이 사실이 아닌 경우를 보았습니다.

여기에 주어진 예는 기록 보존 예입니다.

Human:
    - X - A - B - C - D - F (B1)
           \     / \     /
            G - H ----- I - J (B2)
Real:
            B ----- C - D - F (B1)
           /       / \     /
    - X - A       /   \   /
           \     /     \ /
            G - H ----- I - J (B2)

우리가 인간으로서 왼쪽에서 오른쪽으로, 루트에서 잎까지 (판독) 읽었 기 때문에 여기서도 실제 오해의 소지가 있습니다. 힘내 그렇게하지 않습니다. 우리가 머리에서하는 곳 (A-> B) git does (A <-B or B-> A). ref에서 root까지 읽습니다. 심판은 어느 곳에 나있을 수 있지만 적어도 활동적인 가지에서는 잎사귀 경향이 있습니다. 심판은 커밋을 가리키고 커밋은 자녀가 아닌 부모와 같은 것을 포함합니다. 커밋이 병합 커밋 인 경우 둘 이상의 부모가 있습니다. 첫 번째 부모는 항상 병합 된 원래 커밋입니다. 다른 부모는 항상 원래 커밋으로 병합 된 커밋입니다.

Paths:
    F->(D->(C->(B->(A->X)),(H->(G->(A->X))))),(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))
    J->(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))

이것은 매우 효율적인 표현이 아니라 git이 각 참조 (B1 및 B2)에서 취할 수있는 모든 경로의 표현입니다.

Git의 내부 저장소는 다음과 같습니다 (부모로서 A가 두 번 나타나는 것은 아님).

    F->D,I | D->C | C->B,H | B->A | A->X | J->I | I->H,C | H->G | G->A

원시 자식 커밋을 덤프하면 0 개 이상의 부모 필드가 표시됩니다. 0이 없으면 부모가없고 커밋이 루트임을 의미합니다 (실제로 여러 루트를 가질 수 있음). 있는 경우 병합이없고 루트 커밋이 아니라는 의미입니다. 둘 이상이 있으면 커밋이 병합의 결과이고 첫 번째 이후의 모든 부모가 병합 커밋임을 의미합니다.

Paths simplified:
    F->(D->C),I | J->I | I->H,C | C->(B->A),H | H->(G->A) | A->X
Paths first parents only:
    F->(D->(C->(B->(A->X)))) | F->D->C->B->A->X
    J->(I->(H->(G->(A->X))) | J->I->H->G->A->X
Or:
    F->D->C | J->I | I->H | C->B->A | H->G->A | A->X
Paths first parents only simplified:
    F->D->C->B->A | J->I->->G->A | A->X
Topological:
    - X - A - B - C - D - F (B1)
           \
            G - H - I - J (B2)

둘 다 A를 칠 때 그들의 사슬은 같을 것이고, 그들의 사슬이 완전히 다를 것입니다. 또 다른 두 가지 커밋이 공통적으로 갖는 첫 번째 커밋은 공통 조상이며 어디서 시작했는지입니다. commit, branch, ref라는 용어가 혼동 될 수 있습니다. 실제로 커밋을 병합 할 수 있습니다. 이것이 병합이 실제로하는 일입니다. 참조는 단순히 커밋을 가리키고 분기는 폴더 .git / refs / heads의 참조에 지나지 않으며 폴더 위치는 참조가 태그와 같은 것이 아니라 분기라는 것을 결정합니다.

역사를 잃어버린 곳에서 병합은 상황에 따라 두 가지 중 하나를 수행합니다.

치다:

      / - B (B1)
    - A
      \ - C (B2)

이 경우 어느 방향 으로든 병합하면 현재 체크 아웃 된 분기에서 지정한 커밋으로 첫 번째 부모와 현재 분기에 병합 한 분기의 끝에서 커밋으로 두 번째 부모를 사용하여 새 커밋을 만듭니다. 공통된 조상 이후 결합되어야하는 두 가지 브랜치에 변경 사항이 있으므로 새 커밋을 만들어야합니다.

      / - B - D (B1)
    - A      /
      \ --- C (B2)

이 시점에서 D (B1)는 이제 두 가지 (자체와 B2)의 변경 세트를 모두 갖습니다. 그러나 두 번째 지점에는 B1의 변경 사항이 없습니다. B1에서 B2로 변경 사항을 병합하여 동기화되도록하면 다음과 같은 것을 기대할 수 있습니다 (git 병합을 강제로 --no-ff로 수행 할 수 있음).

Expected:
      / - B - D (B1)
    - A      / \
      \ --- C - E (B2)
Reality:
      / - B - D (B1) (B2)
    - A      /
      \ --- C

B1에 추가 커밋이 있어도 얻을 수 있습니다. B1에없는 변경 사항이 B2에없는 한 두 분기가 병합됩니다. 하나의 브랜치 만 변경 세트를 가지고 있기 때문에 rebase와 달리 하나의 브랜치에서 다른 브랜치의 변경 세트를 적용 할 필요가 없다는 점을 제외하고는 rebase와 같은 빨리 감기를 수행합니다 (리베이스도 먹거나 선형화 기록).

From:
      / - B - D - E (B1)
    - A      /
      \ --- C (B2)
To:
      / - B - D - E (B1) (B2)
    - A      /
      \ --- C

B1에 대한 작업을 중단하면 장기적으로 역사를 보존 할 수 있습니다. B1 (마스터 일 수 있음) 만 일반적으로 진행되므로 B2 기록에서 B2의 위치는 B1에 병합 된 지점을 성공적으로 나타냅니다. 이것은 git이 A에서 B로 분기하기를 기대 한 다음 변경 사항이 누적되는만큼 원하는만큼 A를 B로 병합 할 수 있지만 B를 A로 다시 병합 할 때 B에서 더 이상 작업하지 않을 것으로 예상됩니다 . 지점으로 빨리 병합 한 후 지점에서 계속 작업하는 경우 작업중인 지점에서 매번 B의 이전 기록을 지 웁니다. 소스에 빨리 감기 커밋 한 다음 분기에 커밋 한 후에 매번 새 분기를 만들고 있습니다.

         0   1   2   3   4 (B1)
        /-\ /-\ /-\ /-\ /
    ----   -   -   -   -
        \-/ \-/ \-/ \-/ \
         5   6   7   8   9 (B2)

1 ~ 3 및 5 ~ 8은 4 또는 9의 히스토리를 따르는 경우 표시되는 구조 분기입니다. git에서 명명되지 않은 참조되지 않은 구조 분기 중 명명되지 않은 참조 분기와 참조 분기 중 어느 것이 속하는지 알 수있는 방법은 없습니다. 구조의 끝. 이 그림에서 0 ~ 4는 B1에 속하고 4 ~ 9는 B2에 속하지만 4와 9를 제외하고 어느 브랜치에 어떤 브랜치가 속하는지 알 수 없었습니다. 그 환상. 0은 B2에 속하고 5는 B1에 속할 수 있습니다. 이 경우 16 개의 서로 다른 가능성이 있으며,이 경우 각각의 구조 분기가 속하는 분기를 명명 할 수 있습니다.

이 문제를 해결하는 여러 가지 git 전략이 있습니다. git merge가 빨리 감기되지 않도록하고 항상 merge branch를 만들 수 있습니다. 브랜치 히스토리를 유지하는 끔찍한 방법은 선택한 규칙에 따라 태그 및 / 또는 브랜치 (태그가 실제로 권장 됨)를 사용하는 것입니다. 나는 정말로 당신이 합류하고있는 지점에서 더미 빈 커밋을 권장하지 않을 것입니다. 매우 일반적인 규칙은 실제로 지점을 닫고 싶을 때까지 통합 지점으로 병합하지 않는 것입니다. 이것은 그렇지 않으면 지점이있는 지점에서 작업 할 때 사람들이 준수하려고 시도하는 관행입니다. 그러나 현실 세계에서 이상적인 것이 항상 실용적인 의미는 아닙니다. 옳은 일을하는 것이 모든 상황에 적합한 것은 아닙니다. 당신이 무엇을


"Git은 참조 기록을 보존하지 않습니다"기본적으로는 아니고 광범위한 시간 동안은 아닙니다. man git-reflog날짜와 관련한 부분을 참조하십시오 : "master@{one.week.ago}는"이 지역 저장소에서 일주일 전을 가리키는 데 사용한 마스터 "를 의미합니다." 또는에 대한 논의 <refname>@{<date>}에서 man gitrevisions. 그리고 core.reflogExpire에서 man git-config.
Patrick Mevzek

2

질문에 대한 해결책은 아니지만 오래 가지가있는 지사에서 내가 사용하는 접근법에 주목할 가치가 있다고 생각했습니다.

동시에 지점을 만들면 이름은 같지만 -init접미사 (예 : feature-branch및)를 사용하여 태그를 만듭니다 feature-branch-init.

(이것이 대답하기 어려운 질문이라는 것은 기이 한 일입니다!)


1
언제 어디서 만들어 졌는지에 대한 개념없이 "지점"이라는 개념을 디자인하는 엄청나게 어리석은 어리 석음을 고려할 때 다른 제안 된 솔루션의 엄청난 합병증-사람들이 이처럼 현명하지 못한 방법으로 당신의 솔루션을 선호한다고 생각합니다. 분기를 만들 때 마다이 작업을 수행해야한다는 것을 기억해야합니다 .git 사용자가 자주하는 일입니다. 또한-나는 '태그'가 '무거운'이라는 형벌을 가지고 있다고 어딘가 읽었습니다. 아직도, 나는 그것이 내가 할 것이라고 생각합니다.
Motti Shneor

1

분기 지점을보다 쉽게 ​​볼 수있는 간단한 방법 git log --graph은 옵션을 사용하는 것 --first-parent입니다.

예를 들어, 승인 된 답변 에서 리포지토리 를 가져옵니다. .

$ git log --all --oneline --decorate --graph

*   a9546a2 (HEAD -> master, origin/master, origin/HEAD) merge from topic back to master
|\  
| *   648ca35 (origin/topic) merging master onto topic
| |\  
| * | 132ee2a first commit on topic branch
* | | e7c863d commit on master after master was merged to topic
| |/  
|/|   
* | 37ad159 post-branch commit on master
|/  
* 6aafd7f second commit on master before branching
* 4112403 initial commit on master

이제 추가하십시오 --first-parent:

$ git log --all --oneline --decorate --graph --first-parent

* a9546a2 (HEAD -> master, origin/master, origin/HEAD) merge from topic back to master
| * 648ca35 (origin/topic) merging master onto topic
| * 132ee2a first commit on topic branch
* | e7c863d commit on master after master was merged to topic
* | 37ad159 post-branch commit on master
|/  
* 6aafd7f second commit on master before branching
* 4112403 initial commit on master

그것은 더 쉬워집니다!

repo에 많은 분기가있는 경우 다음을 사용하는 대신 비교하는 2 개의 분기를 지정하려고합니다 --all.

$ git log --decorate --oneline --graph --first-parent master origin/topic

0

문제 는 한 쪽의 두 가지 분기와 다른 쪽의 가장 오래된 공통 조상 사이 에서 가장 최근의 단일 커밋 (아마도 리포의 초기 커밋) 을 찾는 것으로 보입니다 . 이것은 "분기점"이 무엇인지에 대한 직관과 일치합니다.

git rev-list가장 강력한 도구 인 커밋에 도달하는 경로를 제한 수 없으므로 일반적인 git shell 명령으로 계산하기가 쉽지 않습니다 . 우리가 가장 가까운 git rev-list --boundary것은 "우리의 길을 막는"모든 커밋 세트를 줄 수 있습니다. (노트:git rev-list --ancestry-path 흥미롭지 만 여기서 유용하게 만드는 방법은 없습니다.)

여기에 스크립트입니다 : https://gist.github.com/abortz/d464c88923c520b79e3d . 비교적 간단하지만 루프로 인해 요지를 보장하기에 복잡합니다.

여기에 제안 된 대부분의 다른 솔루션은 간단한 이유로 모든 상황에서 작동 git rev-list --first-parent하지 않을 수 있습니다. 두 가지 순서와 병합 될 수 있기 때문에 기록을 선형화하는 데 신뢰할 수 없습니다.

git rev-list --topo-order반면 에 지형 순서대로 커밋을 걷는 데는 매우 유용하지만 diff를 수행하는 것은 쉽지 않습니다. 주어진 그래프에 대해 가능한 여러 지형 순서가 있으므로 순서의 특정 안정성에 따라 결정됩니다. 즉, strongk7의 솔루션은 아마도 대부분 잘 작동합니다. 그러나 리포지토리의 전체 역사를 두 번 걸어야 한 결과 내 것이 느립니다. :-)


0

다음은 svn log --stop-on-copy에 해당하는 git을 구현 하며 분기 원점을 찾는 데 사용할 수도 있습니다.

접근하다

  1. 모든 지점으로 향하십시오
  2. 대상 지점에 대한 mergeBase를 수집합니다.
  3. git.log와 반복
  4. mergeBase 목록에 나타나는 첫 번째 커밋에서 중지

모든 강이 바다로 흘러가는 것처럼, 모든 가지가 지배하기 때문에 우리는 겉으로는 관련이없는 가지 사이의 합병 기반을 찾습니다. 브랜치 헤드에서 조상을 거슬러 올라 가면서 이론 상으로는이 브랜치의 원점이되어야하기 때문에 첫 번째 잠재적 병합 기반에서 멈출 수 있습니다.

노트

  • 형제와 사촌 지점이 서로 병합 된이 접근법을 시도하지 않았습니다.
  • 더 나은 솔루션이 있어야한다는 것을 알고 있습니다.

세부 정보 : https://stackoverflow.com/a/35353202/9950


-1

다음 명령을 사용하여 master에서 도달 할 수없는 branch_a에서 가장 오래된 커밋을 반환 할 수 있습니다.

git rev-list branch_a ^master | tail -1

아마도 추가 커밋 검사를 통해 해당 커밋의 부모 실제로 마스터에서 도달 할 수 있는지 확인하십시오 ...


1
작동하지 않습니다. branch_a가 master에 한 번 병합 된 다음 계속되면 해당 병합에 대한 커밋이 master의 일부로 간주되므로 ^ master에 표시되지 않습니다.
FelipeC

-2

브랜치 A의 reflog를 조사하여 어느 커밋에서 생성 된 커밋과 해당 브랜치가 가리키는 커밋의 전체 히스토리를 찾을 수 있습니다. Reflogs에 .git/logs있습니다.


2
reflog를 제거 할 수 있기 때문에 이것이 일반적으로 효과가 있다고 생각하지 않습니다. 그리고 (?) reflogs도 푸시되지 않는다고 생각하므로 단일 repo 상황에서만 작동합니다.
GaryO

-2

여기에 언급 된 모든 코너 케이스를 처리하는 방법을 찾았습니다.

branch=branch_A
merge=$(git rev-list --min-parents=2 --grep="Merge.*$branch" --all | tail -1)
git merge-base $merge^1 $merge^2

Charles Bailey는 조상 순서에 따른 솔루션의 가치가 제한적이라고 생각합니다. 하루가 끝나면 "이 커밋은 지점 X에서 온 것"이라는 기록이 필요하지만 그러한 기록은 이미 존재합니다. 기본적으로 'git merge'는 "Merge branch 'branch_A'를 마스터로 병합"과 같은 커밋 메시지를 사용합니다. 이는 두 번째 상위 (commit ^ 2)의 모든 커밋이 'branch_A'에서 왔으며 첫 번째로 병합되었음을 나타냅니다. 부모 (커밋 ^ 1), '마스터'.

이 정보로 무장하면 'branch_A'의 첫 번째 병합 ( 'branch_A'가 실제로 존재하는 시점)을 찾고 분기 지점이 될 merge-base를 찾을 수 있습니다. :)

Mark Booth와 Charles Bailey의 저장소를 사용해 보았으며 솔루션이 작동합니다. 어땠어? 이것이 작동하지 않는 유일한 방법은 분기 정보가 실제로 손실되도록 병합에 대한 기본 커밋 메시지를 수동으로 변경 한 경우입니다.

유용성을 위해 :

[alias]
    branch-point = !sh -c 'merge=$(git rev-list --min-parents=2 --grep="Merge.*$1" --all | tail -1) && git merge-base $merge^1 $merge^2'

그럼 당신은 할 수 있습니다 'git branch-point branch_A '를 .

즐겨 ;)


4
병합 메시지에 의존하는 것은 상위 순서에 대한 가설보다 취약합니다. 단순한 가상의 상황이 아닙니다. 내가 자주 사용하는 git merge -m말을 어떻게 내가 오히려 potentionally 임시 지점의 이름 대신에 합병 한 (예 : "기능 XYZ의 리팩토링에 병합 주류 변경"). 예를 들어서 도움이 덜했다고 가정 해 봅시다 -m. 하나 또는 두 개의 임시 브랜치로 동일한 기록을 만들 수 있으며 차이점을 알 수있는 방법이 없기 때문에 문제는 전체 일반성에 녹지 않습니다.
CB Bailey

1
@CharlesBailey 그게 당신의 문제입니다. 커밋 메시지에서 해당 줄을 제거하지 말고 나머지 메시지를 원래 메시지 아래에 추가해야합니다 . 요즘 'git merge'는 원하는 것을 추가 할 수있는 편집기를 자동으로 열고, 이전 버전의 git에서는 'git merge --edit'를 수행 할 수 있습니다. 어느 쪽이든, 커밋 후크를 사용하여 원하는 경우 "모든 커밋에"Commited on branch 'foo' "를 추가 할 수 있습니다. 그러나이 솔루션은 대부분의 사람들에게 효과적 입니다 .
FelipeC

2
나를 위해 일하지 않았다. branch_A는 이미 많은 병합을 가진 마스터에서 분기되었습니다. 이 논리는 branch_A가 작성된 정확한 커밋 해시를 제공하지 않았습니다.
Venkat Kotra
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.