Git 브랜치의 가장 가까운 부모를 찾는 방법은 무엇입니까?
다음과 같은 커밋 트리가있는 다음 로컬 저장소가 있다고 가정 해 봅시다.
master --> a
\
\
develop c --> d
\
\
feature f --> g --> h
master
내 인 이 최신 안정 릴리스 코드가되고 , develop
내 이가 '다음'릴리스 코드 , 및 feature
입니다 준비중인 새로운 기능develop
.
후크를 사용하여 원격 리포지토리에서 수행 할 수있는 것은 feature
커밋 f
이 develop
HEAD 의 직접적인 자손이 아닌 한 푸시를 거부하는 것 입니다. 즉, 기능이 git rebase
켜져 있기 때문에 커밋 트리는 다음과 같습니다 d
.
master --> a
\
\
develop c --> d
\
\
feature f --> g --> h
따라서 다음이 가능합니다.
feature
? 의 부모 분기를 식별하십시오 .f
후손 인 부모 브랜치에서 커밋을 식별 합니까?
거기에서 상위 브랜치의 HEAD가 무엇인지 확인하고 f
이전 모델이 상위 브랜치 HEAD와 일치하는지 확인하여 기능을 리베이스해야하는지 결정합니다.
원격 저장소에 개발 브랜치 의 사본이 있다고 가정하면 (초기 설명은 로컬 저장소에서 설명하지만 원격에도 존재하는 것처럼 들린다) 원하는 생각을 얻을 수 있지만 접근 방식은 당신이 상상 한 것과는 조금 다릅니다.
Git의 역사는 커밋 의 DAG 를 기반으로합니다 . 분기 (및 일반적으로 "참조")는 지속적으로 커밋되는 커밋 DAG에서 특정 커밋을 가리키는 일시적 레이블입니다. 따라서 분기 간의 관계는 시간이 지남에 따라 달라질 수 있지만 커밋 간의 관계는 변하지 않습니다.
---o---1 foo
\
2---3---o bar
\
4
\
5---6 baz
그것은 baz
(이전 버전)을 기반으로하는 것 같습니다 bar
. 그러나 우리가 삭제하면 어떻게 bar
될까요?
---o---1 foo
\
2---3
\
4
\
5---6 baz
이제는 baz
에 기반한 것 같습니다 foo
. 그러나의 조상은 baz
바뀌지 않았습니다. 우리는 레이블 (및 결과 매달려있는 커밋)을 제거했습니다. 에 새 라벨을 추가하면 4
어떻게 되나요?
---o---1 foo
\
2---3
\
4 quux
\
5---6 baz
이제는 baz
에 기반한 것 같습니다 quux
. 그럼에도 불구하고 조상은 바뀌지 않았고 라벨 만 바뀌 었습니다.
그러나 우리가“커밋 6
의 자손을 커밋 3
합니까?” (가정 3
및 6
여부, 다음 대답은 "예"가 될 것입니다 SHA-1은 이름을 커밋 가득) bar
와 quux
라벨이 존재하거나하지 않습니다.
따라서 "푸시 된 커밋은 개발 브랜치 의 현재 팁의 후손 입니까?"와 같은 질문을 할 수 있지만 "푸시 된 커밋의 부모 분기는 무엇입니까?"라고 확실하게 요청할 수는 없습니다.
당신이 원하는 것에 가장 가까운 것으로 보이는 가장 신뢰할만한 질문은 다음과 같습니다.
모두를 위해 (현재의 팁을 제외한의 조상 커밋 밀어 개발 의 현재 끝이 있고, 그 조상) 개발을 부모로를 :
- 그러한 커밋이 하나 이상 존재합니까?
- 그러한 모든 커밋은 단일 부모 커밋입니까?
다음과 같이 구현할 수 있습니다.
pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
echo "'$basename' is missing, call for help!"
exit 1
fi
parents_of_children_of_base="$(
git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
grep -F "$baserev"
)"
case ",$parents_of_children_of_base" in
,) echo "must descend from tip of '$basename'"
exit 1 ;;
,*\ *) echo "must not merge tip of '$basename' (rebase instead)"
exit 1 ;;
,*) exit 0 ;;
esac
이것은 당신이 제한하고 싶은 것의 일부를 다룰 것이지만, 전부는 아닙니다.
참고로 다음은 확장 된 예제 기록입니다.
A master
\
\ o-----J
\ / \
\ | o---K---L
\ |/
C--------------D develop
\ |\
F---G---H | F'--G'--H'
| |\
| | o---o---o---N
\ \ \ \
\ \ o---o---P
\ \
R---S
위의 코드는 거부하는 데 사용할 수 있습니다 H
및 S
수용하면서 H'
, J
, K
, 또는 N
, 그러나 또한 받아 들일 L
와 P
(그들은 병합을 포함, 그러나 끝 병합하지 않는 개발을 ).
또한 거부 L
하고 P
, 당신은 질문을 변경 요청할 수 있습니다
푸시 된 커밋의 조상 (현재 개발 팁과 조상 제외 ) :
- 두 부모와의 약속이 있습니까?
- 그렇지 않다면, 그러한 커밋 중 하나 이상이 현재 유일한 부모 를 개발 하고 있습니까?
pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
echo "'$basename' is missing, call for help!"
exit 1
fi
parents_of_commits_beyond_base="$(
git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
grep -v '^commit '
)"
case "$parents_of_commits_beyond_base" in
*\ *) echo "must not push merge commits (rebase instead)"
exit 1 ;;
*"$baserev"*) exit 0 ;;
*) echo "must descend from tip of '$basename'"
exit 1 ;;
esac
찬사
질문을 표현하는 또 다른 방법은 "현재 분기 이외의 분기에 상주하는 가장 가까운 커밋은 무엇이며 어떤 분기입니까?"입니다.
해결책
약간의 커맨드 라인 마술로 찾을 수 있습니다.
git show-branch -a \
| grep '\*' \
| grep -v `git rev-parse --abbrev-ref HEAD` \
| head -n1 \
| sed 's/.*\[\(.*\)\].*/\1/' \
| sed 's/[\^~].*//'
작동 방식은 다음과 같습니다.
- 원격 브랜치를 포함하여 모든 커밋의 텍스트 기록을 표시합니다.
- 현재 커밋의 조상은 별표로 표시됩니다. 다른 모든 것을 걸러 내십시오.
- 현재 브랜치의 모든 커밋을 무시하십시오.
- 첫 번째 결과는 가장 가까운 조상 지점입니다. 다른 결과는 무시하십시오.
- 지점 이름이 괄호 안에 표시됩니다. 대괄호와 대괄호 밖의 모든 것을 무시하십시오.
- 때로는 분기 이름에 ~ # 또는 ^ #이 포함되어 참조 된 커밋과 분기 팁 사이의 커밋 수를 나타냅니다. 우리는 상관하지 않습니다. 그들을 무시하라.
그리고 결과
위의 코드를 실행
A---B---D <-master
\
\
C---E---I <-develop
\
\
F---G---H <-topic
당신을 줄 것이다 develop
당신이 H에서 실행한다면 master
당신은 I.에서 실행하는 경우
시도해 볼 수도 있습니다 :
git log --graph --decorate
자식 부모
당신은 명령을 실행할 수 있습니다
git parent
@Joe Chrysler 의 답변을 자식 별칭 으로 추가하면 분기의 부모를 찾을 수 있습니다 . 사용법을 단순화합니다.
"~/.gitconfig"
텍스트 편집기를 사용하여 위치한 gitconfig 파일을 엽니 다 . (리눅스의 경우). Windows의 경우 ".gitconfig"경로는 일반적으로 다음 위치에 있습니다.c:\users\your-user\.gitconfig
vim ~/.gitconfig
파일에 다음 별명 명령을 추가하십시오.
[alias]
parent = "!git show-branch | grep '*' | grep -v \"$(git rev-parse --abbrev-ref HEAD)\" | head -n1 | sed 's/.*\\[\\(.*\\)\\].*/\\1/' | sed 's/[\\^~].*//' #"
편집기를 저장하고 종료하십시오.
명령을 실행 git parent
그게 다야!
전반적인 문제에 대한 해결책이 있지만 ( feature
의 끝에서 내려진 경우 결정 develop
), 설명 한 방법으로는 작동하지 않습니다.
당신은 사용할 수 git branch --contains
의 끝에서 내려 모든 지점을 나열 develop
한 후, 사용 grep
확인하기 위해 feature
그들 중 하나입니다.
git branch --contains develop | grep "^ *feature$"
그 중 하나 인 경우 " feature"
표준 출력으로 인쇄 하고 리턴 코드는 0입니다. 그렇지 않으면 아무것도 인쇄하지 않고 리턴 코드는 1입니다.
이것은 나를 위해 잘 작동합니다.
git show-branch | grep '*' | grep -v "$(git rev-parse --abbrev-ref HEAD)" | head -n1 | sed 's/.*\[\(.*\)\].*/\1/' | sed 's/[\^~].*//'
@droidbot 및 @Jistanidiot의 답변
위의 답변 중 어느 것도 우리 저장소에서 작동하지 않았으므로 최신 병합을 사용하여 내 방식을 공유하고 싶습니다 git log
.
#!/bin/bash
git log --oneline --merges "$@" | grep into | sed 's/.* into //g' | uniq --count | head -n 10
라는 스크립트에 넣으십시오.이 스크립트 git-last-merges
는 분기 이름을 현재 분기 대신 git log
인수 및 다른 인수로 허용합니다.
출력에서 자체 분기 규칙과 각 분기의 병합 수를 기반으로 상위 분기를 수동으로 감지 할 수 있습니다.
편집 :git rebase
자식 분기에서 자주 사용 하고 병합이 자주 전달되므로 병합 커밋이 너무 많지 않으면이 답변이 제대로 작동하지 않으므로 미리 커밋을 계산하는 스크립트를 작성했습니다 (정상 및 병합) 현재 브랜치와 비교하여 모든 브랜치에서, 및 커밋 뒤 (부모 브랜치에서 뒤 병합은 안됨) 이 스크립트를 실행하고 당신에게 효과가 있는지 알려주세요.
#!/bin/bash
HEAD="`git rev-parse --abbrev-ref HEAD`"
echo "Comparing to $HEAD"
printf "%12s %12s %10s %s\n" "Behind" "BehindMerge" "Ahead" "Branch"
git branch | grep -v '^*' | sed 's/^\* //g' | while read branch ; do
ahead_merge_count=`git log --oneline --merges $branch ^$HEAD | wc -l`
if [[ $ahead_merge_count != 0 ]] ; then
continue
fi
ahead_count=`git log --oneline --no-merges $branch ^$HEAD | wc -l`
behind_count=`git log --oneline --no-merges ^$branch $HEAD | wc -l`
behind_merge_count=`git log --oneline --merges ^$branch $HEAD | wc -l`
behind="-$behind_count"
behind_merge="-M$behind_merge_count"
ahead="+$ahead_count"
printf "%12s %12s %10s %s\n" "$behind" "$behind_merge" "$ahead" "$branch"
done | sort -n
"Git : 커밋이 시작된 분기 찾기"에 설명 된대로 git branch --contains <commit>
시작 이더라도 커밋이 이루어진 분기를 쉽게 찾아 낼 수 없습니다 (분기의 이름을 바꾸거나, 이동하거나, 삭제할 수 있습니다 ...) .
- 분기 및 목록 분기를
git branch --contains <commit>
나열하지 않을 때까지 커밋에서 커밋으로 돌아갈 수 있습니다 .feature
develop
- 그 커밋 SHA1과 비교
/refs/heads/develop
두 커밋 id가 일치하면 가야합니다 ( feature
분기가 HEAD에서 원점을 가짐을 의미합니다 develop
).
JoeChrysler의 명령 줄 마술을 단순화 할 수 있습니다. 작성된 논리는 다음과 같습니다.
git show-branch -a |
ack '\*' | # we want only lines that contain an asterisk
ack -v "$current_branch" | # but also don't contain the current branch
head -n1 | # and only the first such line
sed 's/.*\[\(.*\)\].*/\1/' | # really, just the part of the line between []
sed 's/[\^~].*//' # and with any relative refs (^, ~n) removed
상대적으로 간단한 awk
명령 으로 5 개의 개별 명령 필터와 동일한 작업을 수행 할 수 있습니다 .
git show-branch -a | awk -F'[]^~[]' '/\*/ && !/'"$current_branch"'/ {print $2;exit}'
다음과 같이 분류됩니다.
-F'[]^~[]'
에서 필드에 선을 분할 ]
, ^
, ~
, 및 [
문자.
/\*/
별표가 포함 된 줄 찾기
&& !/'"$current_branch"'/
...하지만 현재 지점 이름이 아닙니다
{ print $2;
그러한 행을 찾으면 두 번째 필드 (즉, 첫 번째 필드와 두 번째 필드 구분 문자 사이의 부분)를 인쇄하십시오. 간단한 브랜치 이름의 경우, 대괄호 사이에있는 것입니다. 상대 점프가있는 심판의 경우 수정자가없는 이름 일뿐입니다. 따라서 필드 구분 기호 세트는 두 sed
명령 의 의도를 처리 합니다.
exit }
그런 다음 즉시 종료하십시오. 즉, 첫 번째 일치하는 줄만 처리하므로 출력을 통해 파이프 할 필요가 없습니다 head -n 1
.
나는 이것이이 문제를 해결하는 좋은 방법이라고 말하지는 않지만 이것이 효과가있는 것처럼 보입니다.
git branch --contains $(cat .git/ORIG_HEAD)
문제는 파일을 cating하는 것이 git의 내부 작업을 엿보기 때문에 반드시 순방향 호환 (또는 역 호환) 할 필요는 없습니다.
Mark Reed 솔루션의 PowerShell 구현은 다음과 같습니다.
git show-branch -a | where-object { $_.Contains('*') -eq $true} | Where-object {$_.Contains($branchName) -ne $true } | select -first 1 | % {$_ -replace('.*\[(.*)\].*','$1')} | % { $_ -replace('[\^~].*','') }
해결책
이 솔루션 에 기반git show-branch
나는 하나를 결합했습니다, 그래서 내가 (아래 참조) 잘 작동하지 않았다 기반으로git log
이 함께 결국 :
git log --decorate --simplify-by-decoration --oneline \ # selects only commits with a branch or tag
| grep -v "(HEAD" \ # removes current head (and branch)
| head -n1 \ # selects only the closest decoration
| sed 's/.* (\(.*\)) .*/\1/' \ # filters out everything but decorations
| sed 's/\(.*\), .*/\1/' \ # picks only the first decoration
| sed 's/origin\///' # strips "origin/" from the decoration
제한 사항 및주의 사항
- HEAD를 분리 할 수 있지만 (많은 CI 도구는 지정된 분기에서 올바른 커밋을 빌드하기 위해 그렇게합니다), 원래 분기와 로컬 분기 는 현재 HEAD 와 동등하거나 "위"에 있어야합니다.
- 방해 가 되지 않는 태그 가 없어야합니다 . (나는 가정합니다; 자식과 부모 브랜치 사이의 태그를 사용하여 커밋에서 스크립트를 테스트하지 않았습니다)
- 스크립트는 사실에 의존 "HEAD" 항상 최초의 장식으로 나열 바이
log
명령 - 실행 스크립트 에
master
와develop
결과 (주로)에서<SHA> Initial commit
결과
A---B---D---E---F <-origin/master, master
\ \
\ \
\ G---H---I <- origin/hotfix, hotfix
\
\
J---K---L <-origin/develop, develop
\
\
M---N---O <-origin/feature/a, feature/a
\ \
\ \
\ P---Q---R <-origin/feature/b, feature/b
\
\
S---T---U <-origin/feature/c, feature/c
로컬 브랜치 존재에도 불구하고 (예 : SHA가 origin/topic
커밋 O
을 직접 체크 아웃 한 이후 에만 존재 ) 스크립트는 다음과 같이 인쇄해야합니다.
- 커밋의 경우
G
,H
,I
(지점hotfix
) →master
- 커밋의 경우
M
,N
,O
(지점feature/a
) →develop
- 커밋의 경우
S
,T
,U
(지점feature/c
) →develop
- 커밋의 경우
P
,Q
,R
(지점feature/b
) →feature/a
- 커밋의 경우
J
,K
,L
(지점develop
) →<sha> Initial commit
* - 커밋의 경우
B
,D
,E
,F
(지점master
) →<sha> Initial commit
*-또는 '커밋이 마스터의 HEAD보다 위에있는 master
경우 develop
(~ 마스터는 개발하기가 빠르다)
왜 나를 위해 가지를 보여주지 않았습니까?
다음과 같은 상황에서 기반git show-branch
솔루션 은 신뢰할 수 없음을 입증했습니다.
- 분리 된 헤드 – 분리 된 헤드 케이스를 포함
grep '\*' \
하면`grep '을 교체 할 수 있습니다! – – 그리고 그것은 모든 문제의 시작일뿐입니다 - 스크립트 를
master
develop
실행 하고 결과는develop
각각`` - 에 지점
master
지점 (hotfix/
지점)이와 끝까지develop
자신의 가장 가까운 때문에 부모로서master
분기 부모가 표시되었다!
대신의*
이유.
Ant를 사용한 크로스 플랫폼 구현
<exec executable="git" outputproperty="currentBranch">
<arg value="rev-parse" />
<arg value="--abbrev-ref" />
<arg value="HEAD" />
</exec>
<exec executable="git" outputproperty="showBranchOutput">
<arg value="show-branch" />
<arg value="-a" />
</exec>
<loadresource property="baseBranch">
<propertyresource name="showBranchOutput"/>
<filterchain>
<linecontains>
<contains value="*"/>
</linecontains>
<linecontains negate="true">
<contains value="${currentBranch}"/>
</linecontains>
<headfilter lines="1"/>
<tokenfilter>
<replaceregex pattern=".*\[(.*)\].*" replace="\1"/>
<replaceregex pattern="[\^~].*" replace=""/>
</tokenfilter>
</filterchain>
</loadresource>
<echo message="${currentBranch} ${baseBranch}" />
@Mark Reed : 커밋 라인에 별표가 포함될뿐만 아니라 별표로 시작해야한다는 것을 추가해야합니다! 그렇지 않으면 별표가 포함 된 커밋 메시지도 일치하는 행에 포함됩니다. 따라서 다음과 같아야합니다.
git show-branch -a | awk -F'[]^~[]' '/^\*/ && !/'"$current_branch"'/ {print $2;exit}'
또는 긴 버전 :
git show-branch -a |
awk '^\*' | # we want only lines that contain an asterisk
awk -v "$current_branch" | # but also don't contain the current branch
head -n1 | # and only the first such line
sed 's/.*\[\(.*\)\].*/\1/' | # really, just the part of the line between []
sed 's/[\^~].*//' # and with any relative refs (^, ~n) removed`
vbc=$(git rev-parse --abbrev-ref HEAD)
vbc_col=$(( $(git show-branch | grep '^[^\[]*\*' | head -1 | cut -d* -f1 | wc -c) - 1 ))
swimming_lane_start_row=$(( $(git show-branch | grep -n "^[\-]*$" | cut -d: -f1) + 1 ))
git show-branch | tail -n +$swimming_lane_start_row | grep -v "^[^\[]*\[$vbc" | grep "^.\{$vbc_col\}[^ ]" | head -n1 | sed 's/.*\[\(.*\)\].*/\1/' | sed 's/[\^~].*//'
Mark Reed의 답변과 같은 목적을 달성하지만 많은 시나리오에서 오작동하지 않는 훨씬 안전한 접근 방식을 사용합니다.
- 부모 브랜치의 마지막 커밋은 병합이므로 열이 표시
-
되지 않습니다.*
- 커밋 메시지에 지점 이름이 포함됨
- 커밋 메시지 포함
*
요즘이 일을하고 싶은 사람-Atlassian의 SourceTree 애플리케이션은 브랜치가 서로 관련되는 방식, 즉 시작 지점과 현재 커밋 순서 (예 : HEAD 또는 4 개의 커밋 등)를 시각적으로 잘 보여줍니다. .
소스 트리를 사용하면 커밋 세부 정보> 부모>를 보면 커밋 번호에 밑줄이 나타납니다 (링크)
대안: git rev-list master | grep "$(git rev-list HEAD)" | head -1
내 지점과 master
(또는 지정하려는 지점) 모두 마지막 커밋을 가져옵니다.
이 같은 일을했을 때 나에게 효과가 없었습니다. develop > release-v1.0.0 > feature-foo
개발하기 위해 다시 돌아갈 것입니다. 리베이스가 관련되어 있음을 주목하십시오.
다음은 올바른 커밋 해시를 제공했습니다.
git log --decorate \
| grep 'commit' \
| grep 'origin/' \
| head -n 2 \
| tail -n 1 \
| awk '{ print $2 }' \
| tr -d "\n"
참고 URL : https://stackoverflow.com/questions/3161204/how-to-find-the-nearest-parent-of-a-git-branch
'Programming' 카테고리의 다른 글
오리진의 마스터 브랜치 파일 버전으로 되 돌리는 방법 (0) | 2020.02.26 |
---|---|
AngularJS UI 라우터 로그인 인증 (0) | 2020.02.26 |
Ruby 클래스의 이름은 어떻게 얻습니까? (0) | 2020.02.26 |
Internet Explorer에서 'console'은 정의되지 않은 오류입니다. (0) | 2020.02.26 |
Linq에서 SQL Like %를 수행하는 방법은 무엇입니까? (0) | 2020.02.26 |