자식에서 다음 커밋을 어떻게 찾습니까? (ref의 어린이 / 어린이)


236

ref^전에 커밋을 참조 ref하십시오. 커밋 후에는 ref 어떻습니까?

예를 들어 git checkout 12345다음 커밋을 어떻게 확인합니까?

감사.

추신 : git은 DAG 노드 포인터 구조체 트리입니다. 이 커밋 후에 커밋을 어떻게 찾습니까?



2
" git children-of" 도 참조하십시오 !
VonC

답변:


185

현재 커밋에서 시작하여 커밋을 모두 나열하려면 기본적으로 표준 git log이지만 시간이 지남에 따라 다음과 같은 것을 사용하십시오.

git log --reverse --ancestry-path 894e8b4e93d8f3^..master

여기서 894e8b4e93d8f3은 표시하려는 첫 번째 커밋입니다.


3
원래 질문의 특정 사례는을 대신 HEAD^하십시오 894e8b4e93d8f3^.
Søren Løvborg

2
아마도 --oneline을 추가하는 것이 더 짧은 간단한 결과입니다.
firo

1
내가 사용에 필요 ..., ^..자동으로 실패
TankorSmash

1
fatal: unrecognized argument: --ancestry-path자식 버전 1.7.1에 도착
user151841

6
master현재 커밋의 상위 경로에있는 경우에만 작동합니다 . 모든 경우에 작동하는 솔루션에 대한 내 대답 의 두 번째 코드 스 니펫을 참조하십시오 .
Tom Hale

37

Hudson (현재 Jenkins) Kohsuke Kawaguchi 의 제작자가 방금 게시 (2013 년 11 월) :
kohsuke / git-children-of :

커밋이 주어지면 해당 커밋의 직계 자식을 찾으십시오.

#!/bin/bash -e
# given a commit, find immediate children of that commit.
for arg in "$@"; do
  for commit in $(git rev-parse $arg^0); do
    for child in $(git log --format='%H %P' --all | grep -F " $commit" | cut -f1 -d' '); do
      git describe $child
    done
  done
done

이 스레드 에서 볼 수 있듯이 DAG (Directed Acyclic Graph)로 표시되는 히스토리를 기반으로하는 VCS 에는 "하나의 부모"또는 "하나의 자식"이 없습니다.

        C1 -> C2 -> C3
      /               \
A -> B                  E -> F
      \               /
        D1 -> D2 ----/

커밋 순서는 "topo-order"또는 "date-order"에 의해 수행됩니다 ( GitPro book 참조 ).

그러나 Git1.6.0 부터 커밋의 자식을 나열 할 수 있습니다.

git rev-list --children
git log --children

참고 : 부모 커밋의 경우 동일한 커밋이 있으며 해당 커밋 객체 ^첫 번째 부모를 의미하는 개정 매개 변수 의 접미어 가 있습니다. ^<n><n>부모를 의미합니다 (즉, rev^ 에 해당 rev^1).

지점에 foo있고 " git merge bar"를 발행 foo하면 첫 번째 부모가됩니다.
즉 : 첫 번째 부모는 병합 할 때 있었던 지점이고 두 번째 부모는 병합 한 지점의 커밋입니다.


6
git rev-list --children확실히 내가 원하는 것처럼 보이지만 DWIM은 아닙니다. 모든 부모와 자녀를 나열하는 것으로 보입니다. 나는 그것들을 모두 나열하고 그들을 통해 파싱 할 수 있다고 가정합니다 ... bleh,하지만 그 무언가.
Schwern

@Schwern : 사실, git rev-list --children단지 아이를 나열하는 것이 아니라 부모 상장 자녀와 함께 ... 당신은 항상 구문 분석 할 필요가있다.
VonC

두 명의 자녀가있는 커밋에서 그 코드를 시도했습니다. $ git children0of 9dd5932 fatal: No annotated tags can describe '71d7b5dd89d241072a0a078ff2c7dfec05d52e1f'. However, there were unannotated tags: try --tags. 어떤 결과를 얻습니까?
Tom Hale

@TomHale 지금 테스트 할 수는 없지만 모든 사람이 테스트 할 수있는 새로운 질문 (OS 및 Git 버전)을 묻습니다.
VonC

1
유일한 문제 git-children-ofgit describe 를 사용 하여 SHA를 사람이 읽을 수있는 형식으로 만들려고 시도하는데 @TomHale의 오류로 실패 할 수 있으며 v1.0.4-14-g2414721SHA를 예상하면 혼란 스러울 수 있습니다. 간단한 것으로 교체 echo하면 훌륭한 도구입니다. 감사합니다!
Nickolay

18

내가 찾은 것은

git rev-list --ancestry-path commit1..commit2

내가 설정 한 경우 commit1현재는 커밋하고 commit2현재의 머리. 이것은 나에게 사이의 경로 구축 모든 커밋의 목록 반환 commit1commit2.

출력의 마지막 행은 commit1의 자식입니다 (commit2의 경로에 있음).


3
| tail -1아이를 얻기 위해 추가하십시오 .
Jesse Glick

8

무슨 뜻인지 알아 이전 커밋으로가는 구문이 풍부하지만 다음 커밋으로 이동하는 것은 실망 스럽습니다. 복잡한 역사에서 "다음 커밋은 무엇인가"라는 문제가 다소 어려워 지지만 복잡한 병합에서는 동일한 경도가 '이전'커밋과 함께 나타납니다. 간단한 경우, 선형 히스토리가있는 단일 분기 (일부 제한적 커밋의 경우 로컬조차도)에서는 앞뒤로 이동하는 것이 좋습니다.

그러나 이것의 실제 문제는 자식 커밋이 참조되지 않고 역방향으로 연결된 목록이라는 것입니다. 자식 커밋을 찾는 것은 너무 나쁘지 않지만 git이 refspec 로직에 넣고 싶지 않은 검색을 필요로합니다.

어쨌든, 나는이 질문에 도달했습니다. 왜냐하면 나는 한 번에 한 번의 커밋을 수행하고 테스트를 수행하기를 원하기 때문에 때로는 뒤로 물러서지 않고 앞으로 나아가 야하기 때문입니다. 글쎄, 좀 더 생각하면이 솔루션을 생각해 냈습니다.

현재 위치보다 먼저 커밋을 선택하십시오. 아마도 브랜치 헤드 일 수 있습니다. 분기 ~ 10에 있다면 "git checkout branch ~ 9"다음 "git checkout branch ~ 8"다음에 다음을 얻으려면 "git checkout branch ~ 7"등입니다.

필요한 경우 스크립트에서 숫자를 줄이는 것이 정말 쉬워야합니다. git rev-list를 파싱하는 것보다 훨씬 쉽습니다.


유용하지만 다음 커밋을 찾지 못합니다. 현재 커밋을 향해 걸어갑니다.
Schwern

1
그럼 당신이 할 수 있다고 생각합니다. " BRANCH=master; git co $BRANCH~$[ $(git rev-list HEAD..$BRANCH | wc -l) - 1 ]"당신은 가지를 향해 가지 않으면 안됩니다.
vontrapp

8

두 가지 실질적인 답변 :

한 아이

@Michael의 답변을 바탕으로 child내 별칭을 해킹했습니다 .gitconfig.

기본 경우에 예상대로 작동하며 다목적입니다.

# Get the child commit of the current commit.
# Use $1 instead of 'HEAD' if given. Use $2 instead of curent branch if given.
child = "!bash -c 'git log --format=%H --reverse --ancestry-path ${1:-HEAD}..${2:\"$(git rev-parse --abbrev-ref HEAD)\"} | head -1' -"

현재 분기의 끝을 향한 조상 한 단계를 따라 (다른 커밋이 두 번째 인수로 제공되지 않는 한) 조상을 한 단계 씩 수행하여 HEAD의 자식 (다른 커밋이 필요한 인수가 제공되지 않은 경우)을 제공하는 것이 기본값입니다.

짧은 해시 양식을 원한다면 %h대신 사용하십시오 %H.

여러 자녀

HEAD가 분리되어 있거나 (분기가 없음) 분기에 관계없이 모든 어린이를 갖기 위해 :

# For the current (or specified) commit-ish, get the all children, print the first child 
children = "!bash -c 'c=${1:-HEAD}; set -- $(git rev-list --all --not \"$c\"^@ --children | grep $(git rev-parse \"$c\") ); shift; echo $1' -"

(가) 변경 $1하는 $*모든 아이들을 인쇄 할 수 있습니다.

또한 --all커밋을 변경 하여 해당 커밋의 조상 인 자식 만 표시합니다. 즉, 지정된 커밋의 "방향으로"만 표시합니다. 이를 통해 많은 어린이의 출력을 한 사람으로 좁힐 수 있습니다.


7

고유 한 "다음 커밋"이 없습니다. Git의 히스토리는 한 줄이 아닌 DAG이므로 많은 커밋에는 공통 부모 (분기)가있을 수 있으며, 커밋에는 둘 이상의 부모 (병합)가있을 수 있습니다.

특정 분기를 염두에두면 로그를보고 현재 커밋을 상위로 나열하는 커밋을 확인할 수 있습니다.


37
이 논리에 따르면 "이전 커밋"도 없지만 부모를 얻는 데 필요한 구문이 많이 있습니다.
Schwern

7
@ Schwern : "이전 커밋"도 없습니다. <rev>^"parent commit"(병합 커밋의 '첫 번째 부모')입니다.
Jakub Narębski

6

나는 많은 다른 해결책을 시도했지만 아무도 나를 위해 일하지 않았다. 내 자신을 생각해 내야했다.

다음 커밋 찾기

function n() {
    git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}

이전 커밋 찾기

function p() {
    git checkout HEAD^1
}

6

특정 "대상"이없는 경우에는 마음에 커밋 대신에있을 수 있습니다 자식 커밋보고 싶은 어떤 지점 것은,이 명령을 사용할 수 있습니다 :

git rev-list --children --all | grep ^${COMMIT}

모든 어린이 와 손자 를보고 싶다면 rev-list --children다음과 같이 재귀 적 으로 사용해야합니다 .

git rev-list --children --all | \
egrep ^\($(git rev-list --children --all | \
           grep ^${COMMIT} | \
           sed 's/ /|/g')\)

(손자 에게만 제공되는 버전 은 더 복잡한 sed및 / 또는cut 합니다.)

마지막으로, 다음 log --graph과 같이 트리 구조를보기 위해 명령에이를 공급할 수 있습니다 .

git log --graph --oneline --decorate \
\^${COMMIT}^@ \
$(git rev-list --children --all | \
  egrep ^\($(git rev-list --children --all | \
             grep ^${COMMIT} | \
             sed 's/ /|/g')\))

참고 : 위의 명령은 모두 쉘 변수 ${COMMIT}를 관심있는 커밋의 참조 (branch, tag, sha1)로 설정했다고 가정합니다 .


2
이것은 Google 및 stackoverflow에 대한 공식화 방법을 몰랐지만 묻고 자하는 질문에 대한 답변입니다. 적극적으로 필요성을 인식 해 주셔서 감사합니다
Tommy Knowlton

나를 위해 ${COMMIT}지속되지 않지만 $(git rev-parse HEAD) 대신 사용할 수 있습니다
Radon8472

죄송합니다.에 대한 메모를 추가했습니다 ${COMMIT}.
매트 맥 헨리

5

(이것은 중복 질문에 대한 답변으로 시작되었습니다. 정리하기 위해 약간의 가벼운 편집을했습니다.)

Git의 모든 내부 화살표는 한 방향이며 뒤로 향합니다. 따라서 앞으로 나아갈 수있는 편리한 구문이 없습니다. 단지 불가능합니다.

그것은 이다 당신이 나중에 전에 그것을 본 후 분명하지 않은 경우 "화살표에 대한 이동"하지만 놀라운을 할 수있는 방법으로 가능. 우리가 가지고 있다고 가정 해 봅시다.

A <-B <-C <-D <-E   <-- last
        ^
        |
         \--------- middle

를 사용 middle~2하여 화살표를 C뒤로 두 번 따라 갑니다 A. 그래서 우리는 어떻게에서 이동합니까 CD? 대답은 : 우리가 시작하는 E이름을 사용하여, last우리가 얻을 때까지, 작업의 뒤로 middle, 우리가 길을 따라 방문하는 점을 기록 . 그런 다음 우리는 원하는 방향으로 last움직 입니다 . 한 단계는로 이동 D하거나 두 단계는로 이동 합니다 E.

분기가있을 때 특히 중요합니다.

          D--E   <-- feature1
         /
...--B--C   <-- master
         \
          F--G   <-- feature2

어느 커밋이 한 단계 뒤에 C있습니까? 질문에 추가하기 전까지는 정답이 없습니다 : feature 방향으로 (공백을 채우십시오).

C(제외 C) 자체와 ( ) 사이의 커밋을 열거하기 G위해 다음을 사용합니다.

git rev-list --topo-order --ancestry-path master..feature2

--topo-order만든다 확실히 복잡한 분기 앤 병합의 존재, 커밋은 위상 적-정렬 된 순서로 나올 것이다. 체인이 선형이 아닌 경우에만 필요합니다. --ancestry-path우리가부터 거꾸로 작업 할 때 제약 수단은 것을 feature2가지고 우리 만 나열 커밋 커밋 C자신의 조상 중 하나. 즉, 그래프 또는 그래프의 관련 덩어리가 실제로 다음과 같이 보이는 경우 :

A--B--C   <-- master
 \     \
  \     F--G--J   <-- feature2
   \         /
    H-------I   <-- feature3

형태의 간단한 요청은 feature2..master커밋을 열거 J, GIFH일부 순서. 로 --ancestry-path우리가 노크 H하고 I: 그들은의 후손 없습니다 C만의, A. 함께 --topo-order우리는 실제 열거 순서가 있는지 확인 J후, G다음,F .

git rev-list명령은 이러한 해시 ID를 표준 출력에서 ​​한 줄에 하나씩 흘립니다. 방향으로 한 단계 앞으로 이동하려면 마지막 줄을 feature2원합니다 .

그것은 가능 (유혹하고 유용 할 수 있습니다) 추가 --reverse그래서 git rev-list그들을 생성 후 역순 인쇄 커밋. 작동하지만 다음과 같은 파이프 라인에서 사용하는 경우 :

git rev-list --topo-order --ancestry-path --reverse <id1>...<id2> | head -1

"id2 방향의 다음 커밋"을 얻으려면 커밋 목록이 매우 길기 때문에 git rev-list명령을 쓰려고 할 때 끊어진 파이프를 얻을 수 있습니다 head. 깨진 파이프 오류는 일반적으로 셸에서 무시되므로 대부분 작동합니다. 그들이 당신의 무시에 있는지 확인 하십시오 사용.

또한 명령과 함께 명령 을 추가 -n 1하고 싶습니다 . 하지마! 즉하게 도보로 한 단계 후 중지를 뒤로 하고 방문 커밋의 (한 항목) 목록을 역. 그래서 이것은 단지git rev-list--reversegit rev-list<id2> 매번 합니다.

중요한 사이드 노트

"다이아몬드"또는 "벤젠 고리"그래프 단편으로 다음을 참고하십시오.

       I--J
      /    \
...--H      M--...  <-- last
      \    /
       K--L

하나에서 "앞으로"커밋 이동 H을 향한 것이 last당신을 얻을 것 중 하나 I 또는 K . 그것에 대해 당신이 할 수있는 일은 없습니다 : 두 커밋 모두 한 단계 앞으로입니다! 결과 커밋에서 시작하여 다른 단계로 넘어 가면 이제 시작한 경로에 전념하게됩니다.

이를위한 치료법은 한 번에 한 단계 씩 이동하고 경로 종속 체인에 고정되지 않도록하는 것입니다. 당신이 전체 가계 경로 체인을 방문 할 계획이라면 대신, 다른 작업을 수행하기 전에 , a를 체인에있는 모든 커밋의 목록을 :

git rev-list --topo-order --reverse --ancestry-path A..B > /tmp/list-of-commits

그런 다음이 목록의 각 커밋을 한 번에 하나씩 방문하면 전체 체인을 얻을 수 있습니다. 은 --topo-order당신이 명중 있는지 확인합니다 I- 및 - J순서로, 그리고 K- 및 - L(당신은 KL 쌍 전이나 후에 IJ 쌍을 다하겠습니다 여부를 예측할 수있는 쉬운 방법은 없습니다하지만) 순으로.


" 질문에 추가하기 전까지는 정답이 없습니다 : feature___ 방향으로 "이것은 매우 좋은 지적입니다. 답변 주셔서 감사합니다.
Schwern

4

이 별칭이 있습니다 ~/.gitconfig

first-child = "!f() { git log  --reverse --ancestry-path --pretty=%H $1..${2:-HEAD} | head -1; }; f"


무엇 f()입니까? 그리고 head -1첫 아이가되어야합니다. 그렇지 않으면 단순히 HEAD를보고 할 것입니다.
Xerus

@Xerus "복잡한"쉘 구문을 사용하기 때문에 git은 래핑되지 않은 경우이를 인식하지 못합니다 f(). 예, head -1용감한 추측입니다.
weakish

좋은! 내 조정 : nextref = "!f() { git log --reverse --ancestry-path --pretty=%H $1..HEAD | head -${2:-1} | tail -1; }; f"그래서 당신은 선택적으로 얼마나 멀리 선택할 수 있습니다
Z. Khullah

2

다음 아이를 다음과 같은 방법으로 찾을 수있었습니다.

git log --reverse --children -n1 HEAD (where 'n' is the number of children to show)

1
나를 위해 이것은 첫 번째 아이가 아니라 현재 커밋을 보여줍니다
Radon8472

2

자식 커밋이 모두 일부 브랜치 gitk --all commit^..에있는 경우 "commit"은 커밋을 식별하는 것입니다. 예를 들어, 커밋의 약어 SHA-1이 c6661c5이면 다음을 입력하십시오.gitk --all c6661c5^..

gitk의 "SHA1 ID :"셀에 전체 SHA-1을 입력해야 할 것입니다. 전체 SHA-1이 필요합니다.이 예에서는 다음을 통해 얻을 수 있습니다.git rev-parse c6661c5

또는 git rev-list --all --children | grep '^c6661c5883bb53d400ce160a5897610ecedbdc9d'이 커밋의 모든 자식을 포함하는 라인을 생성 할 것입니다.


0

각 커밋은 부모에 대한 포인터를 저장합니다 (병합 (표준) 커밋의 경우 부모).

따라서 부모로부터 자식 커밋 (있는 경우)을 가리킬 수있는 방법이 없습니다.


커밋은 나중에 자식 커밋 (분기점)을 추가 할 수 있으므로 자식에 대한 포인터를 저장할 수 없습니다.
Jakub Narębski

@Jakub 나는 정말 따라하지 않습니다. 나중에 추가 할 수 없습니까?
Schwern

1
Jakub : 정확히 내가 말한 것입니다. '각 커밋은 부모에게만 포인터를 저장합니다.'
Lakshman Prasad

@ Schwern : 자식 커밋은 변경 불가능하므로 (책임의 결과가 훌륭합니다), 어린이에 대한 포인터는 "나중에 추가되지 않습니다". 이유 중 하나는 커밋 식별자 (예 : "부모"링크에서 사용)가 커밋의 내용에 의존하기 때문입니다. 중앙 번호 매기기 권한이없는 분산 시스템의 유일한 솔루션입니다. 또한 커밋의 "자식"은 가지고있는 브랜치에 따라 다르며, 이는 리포지토리마다 다를 수 있습니다 (커밋은 각 리포지토리에서 동일합니다).
Jakub Narębski

4
@becomingGuru 아래로 투표했습니다. 사실 일 수도 있지만 내 질문에 대답하지 않습니다. 문제는 "git에서 다음 커밋을 어떻게 찾습니까?"입니다. "git commit은 자식에 대한 포인터를 저장하지 않습니까?"
Schwern


0

기존 답변은 찾고있는 커밋이 포함 된 분기가 있다고 가정합니다.

필자의 경우, 내가 찾은 커밋 git rev-list --all에는 분기가 포함되어 있지 않았기 때문에 진행되지 않았습니다.

나는 gitk --reflog수동으로 살펴 보았습니다.

reflog에서도 커밋을 찾을 수 없으면 다음 중 하나를 시도하십시오.

  • git fsck --full 매달려있는 (즉, 분기에없는) 커밋을 나열하거나
  • git fsck --lost-found 매달려있는 커밋을 가리키는 심판을 다른 답변에 기술을 적용합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.