Git'teki bir sonraki taahhüdü nasıl bulabilirim? (ref / child / ref)


Yanıtlar:


185

Geçerli olandan ve sonra alt öğesinden başlayarak tüm taahhütleri listelemek için - temelde standart git günlüğü, ancak zaman içinde diğer yoldan gitmek, gibi bir şey kullanın

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

Burada 894e8b4e93d8f3 göstermek istediğiniz ilk işlemdir.


3
Orijinal sorudaki özel durum için, yerine HEAD^koyun 894e8b4e93d8f3^.
Søren Løvborg

2
Belki, add --oneline daha iyi bir kısa çıktı sonucudur.
firo

1
...^..
Kullanmam

1
fatal: unrecognized argument: --ancestry-pathGit sürüm 1.7.1 Başlarken
user151841

6
Bu yalnızca mastermevcut taahhüdün atalarının yolundaysa işe yarar . Bkz benim cevabım her durumda çalışacak bir çözüm 'ın ikinci kod parçacığını.
Tom Hale

37

Hudson (şimdi Jenkins) yaratıcısı Kohsuke Kawaguchi az önce yayınlandı (Kasım 2013):
kohsuke / git-children-of :

Bir taahhüt verildiğinde, söz konusu taahhütten derhal çocukları bulun.

#!/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

Bu iş parçacığında gösterildiği gibi , bir DAG (Yönlendirilmiş Asiklik Grafiği) ile temsil edilen geçmişe dayanan bir VCS'de "bir ebeveyn" veya "bir çocuk" yoktur.

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

Taahhütlerin sıralanması "topo-order" veya "date-order" ile yapılır ( GitPro kitabına bakın )

Ancak Git1.6.0'dan beri , bir taahhüdün çocuklarını listeleyebilirsiniz.

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

Not: Üst taahhütler için aynı sorunla karşılaşırsınız; ^revizyon parametresinin soneki , söz konusu taahhüt nesnesinin ilk üst öğesi anlamına gelir . ^<n>" <n>Ebeveyn" anlamına gelir (yani buna rev^ eşdeğerdir rev^1).

Eğer şube foove sorun " git merge bar" fooise ilk ebeveyn olacak.
Yani: İlk ebeveyn, birleştirdiğinizde bulunduğunuz dal ve ikincisi, birleştirdiğiniz daldaki taahhüttür.


6
git rev-list --childreneminim istediğim gibi görünüyor, ama DWIM değil. Tüm ebeveynleri ve çocuklarını listeliyor gibi görünüyor. Sanýrým hepsini listeleyebilir ve onlarý ayrıştırabilirim ... bleh, ama bu birţey.
Schwern

@Schwern: true, git rev-list --childrensadece çocukları listelemek için değil, ebeveynleri çocuklarıyla birlikte listelemek için ... her zaman ayrıştırmanız gerekir.
VonC

Bu kodu iki çocukla bir taahhütte denedim: $ git children0of 9dd5932 fatal: No annotated tags can describe '71d7b5dd89d241072a0a078ff2c7dfec05d52e1f'. However, there were unannotated tags: try --tags. Hangi çıktıyı alıyorsunuz?
Tom Hale

@TomHale Şu anda test edemiyorum, ancak yeni bir soru sorun (OS ve Git sürümü ile), böylece herkes test edebilir.
VonC

1
Tek sorun, git'i, @ TomHale'nin hatasıyla başarısız olabilen SHA'yı insan tarafından okunabilir olarak biçimlendirmeye çalıştığını ve bir SHA beklerseniz kafa karıştırıcı olduğunu veren git açıklamasınıgit-children-of kullanmasıdır . Basit ile değiştirmek bunu mükemmel bir araç yapar, teşekkürler! v1.0.4-14-g2414721echo
Nickolay

18

bulduğum şey

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

Ben set nerede commit1şimdiki taahhüt olarak ve commit2mevcut başkanı. Bu bana commit1ve arasında bir yol oluşturan tüm taahhütlerin bir listesini döndürür commit2.

Çıktının son satırı, command1 öğesinin alt öğesidir (commit2 yolunda).


3
Yani | tail -1çocuğu almak için ekleyin .
Jesse Glick

8

Ne demek istediğini biliyorum. Önceki taahhütlere gitmek için bol sözdizimine sahip olmak sinir bozucudur, ancak bir sonraki taahhütlere gitmek için hiçbiri. Karmaşık bir tarihte, "bir sonraki taahhüt nedir" sorunu oldukça zorlaşır, ancak daha sonra aynı sertliğin bir araya getirilmesinde "önceki" taahhütlerle de ortaya çıkar. Basit durumda, doğrusal bir geçmişe sahip tek bir dalın içinde (sadece sınırlı sayıda taahhüt için yerel olarak bile) güzel ve ileri ve geri gitmek mantıklı olacaktır.

Bununla birlikte asıl sorun, çocukların taahhüt ettikleri referanslara başvurulmaması, sadece geriye doğru bağlantılı bir listedir. Çocuk taahhüdünü bulmak, çok kötü olmayan bir arama gerektirir, ancak git'in refspec mantığına koymak istediği bir şey değildir.

Her halükarda, bu soruya geldim çünkü sadece bir seferde bir taahhütte bulunmak, testler yapmak ve bazen geri adım atmak değil, ileriye doğru adım atmak istiyorum. Biraz daha düşünce ile bu çözümü buldum:

Bulunduğunuz yerin önünde bir taahhüt seçin. Bu muhtemelen bir şube başı olabilir. Eğer ~ 10 şubesindeyseniz, o zaman "git checkout branch ~ 9" sonra "git checkout branch ~ 8", ondan sonraki adımı almak için "git checkout branch ~ 7" vb.

Gerekirse, bir komut dosyasında sayıyı azaltmak gerçekten kolay olmalıdır. Git rev listesini ayrıştırmaktan çok daha kolay.


Yararlı olsa da, bir sonraki taahhüdü bulamaz. Mevcut taahhüde doğru yürüyor.
Schwern

1
Öyleyse yapabileceğinizi varsayalım: " BRANCH=master; git co $BRANCH~$[ $(git rev-list HEAD..$BRANCH | wc -l) - 1 ]" Bir şubeye gitmelisiniz, bunun hiçbir yolu yok.
vontrapp

8

İki pratik cevap:

Bir çocuk

Dayanarak Michael'ın cevap @ , ben kesmek childskinTenimde takma .gitconfig.

Varsayılan durumda beklendiği gibi çalışır ve aynı zamanda çok yönlüdür.

# 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' -"

Atadan mevcut dalın ucuna doğru bir adım takip ederek (ikinci argüman olarak başka bir taahhüt vermedikçe) HEAD'ın çocuğuna (başka bir taahhüt-sav argümanı verilmediği sürece) verilmesi varsayılan olarak geçerlidir.

Kısa karma formunu %hkullanmak yerine kullanın %H.

Birden fazla çocuk

Müstakil bir KAFA ile (şube yoktur) veya şubelere bakılmaksızın tüm çocukları almak için:

# 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' -"

Tüm çocukları yazdırmak $1için $*olarak değiştirin .

Ayrıca --allyalnızca bu taahhüdün atası olan çocukları görüntülemek için bir komiteye geçebilirsiniz ; diğer bir deyişle, yalnızca söz konusu taahhüdü “yönünde” gösteren çocukları da gösterebilirsiniz. Bu, çıktıyı birçok çocuktan sadece bir taneye indirmenize yardımcı olabilir.


7

Benzersiz bir "sonraki taahhüt" yoktur. Git'teki geçmiş bir satır değil, bir DAG olduğundan, birçok işlemin ortak bir üst öğesi (dalları) olabilir ve işlemlerin birden fazla üst öğesi olabilir (birleştirmeler).

Aklınızda belirli bir dalınız varsa, günlüğüne bakabilir ve hangi taahhüdün mevcut olanı üst öğe olarak listelediğini görebilirsiniz.


37
Bu mantıkla "önceki taahhüt" de yoktur, ancak ebeveyn (ler) i elde etmek için birçok sözdizimi vardır.
Schwern

7
@Schwern: "Önceki taahhüt" de yoktur; <rev>^"ebeveyn taahhüdü" dür (birleştirme taahhütleri için 'ilk ebeveyn').
Jakub Narębski

6

Birçok farklı çözüm denedim ve hiçbiri benim için çalışmadı. Kendi ile gelmek zorunda kaldı.

bir sonraki taahhüdü bul

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

önceki taahhüdü bul

function p() {
    git checkout HEAD^1
}

6

Belirli bir "hedef" taahhüdünüzün olmadığı, ancak bunun yerine herhangi bir dalda olabilecek alt taahhütleri görmek istiyorsanız, bu komutu kullanabilirsiniz:

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

Tüm çocukları ve büyük çocukları görmek istiyorsanız, tekrar tekrar kullanmalısınız rev-list --children, şöyle:

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

( Sadece büyük çocuklara verilen versiyon daha karmaşık sedve / veya kullanır cut.)

Son olarak, log --graphağaç yapısını görmek için bunu bir komuta aktarabilirsiniz , şöyle:

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

Not : Yukarıdaki komutların tümü, shell değişkenini ${COMMIT}, çocuklarıyla ilgilendiğiniz taahhüdün bazı referanslarına (şube, etiket, sha1) ayarladığınızı varsayar .


2
Bu, google ve stackoverflow için nasıl formüle edileceğini bilmediğim soruyu cevaplıyor, ancak sormaya çalışıyordu. proaktif olarak ihtiyacı tanıdığınız için teşekkür ederiz
Tommy Knowlton

benim için ${COMMIT}değil, ama sen- $(git rev-parse HEAD)
ebilmek

üzgünüm, hakkında bir not eklendi ${COMMIT}.
Matt McHenry

5

(Bu yinelenen bir soruya yanıt olarak başladı. Temizlemek için biraz ışık düzenleme yaptım.)

Git'in tüm iç okları tek yönlüdür ve geriye dönüktür. Bu nedenle, ileriye doğru hareket etmek için kısa ve uygun bir sözdizimi yoktur: bu mümkün değildir.

Bu ise sen sonra önceden görmüş ve ardından belirgin değil eğer "oklarıyla karşı hareket", ancak şaşırtıcı yapmanız şekilde mümkün. Diyelim ki elimizde:

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

Kullanmak middle~2okları Carkadan iki kez takip eder A. Öyleyse nasıl hareket Cediyoruz D? Cevap: Biz başlayacak Eadını kullanarak, lastbiz gelinceye kadar ve çalışma geriye middle, yol boyunca ziyaret noktaları kayıt . Sonra sadece istediğimiz kadar ilerleriz last: bir adım Dya da iki adım E.

Bu, şubelerimiz olduğunda özellikle önemlidir:

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

Bir adım sonra hangi taahhüt var C? Soruya eklenene kadar doğru cevap yoktur: özellik_ yönünde (boşluğu doldurun).

Kendisi arasındaki C(hariç C) taahhütleri numaralandırmak için şunu Gkullanırız:

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

Bu --topo-order, karmaşık dallanma ve birleştirme mevcudiyetinde bile, taahhütlerin topolojik olarak sıralanmış şekilde çıkmasını sağlar. Bu sadece zincir doğrusal değilse gereklidir. --ancestry-pathBiz geriye doğru zaman zorlama aracının o feature2var biz sadece liste kaydedilmesini taahhüt Ckendi atalarından biri olarak. Yani, grafik - ya da yine de onun ilgili parçası - aslında şöyle görünüyorsa:

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

Formun basit bir istek feature2..masterkaydedilmesini sıralar J, Gve I, ve Fve Hbazı sırayla. İle --ancestry-pathbiz nakavt Hve I: onlar soyundan değildir Csadece, A. İle --topo-orderbiz gerçek numaralandırma düzeni olduğundan emin olmak J, sonra Gsonra, F.

git rev-listKomut standart çıktı satıra bir tane üzerinde bu karma kimlikleri dökülür. Bir adım ileri gitmek için feature2, sadece son çizgiyi istiyoruz .

Mümkün (ve cazip ve kullanışlı olabilir) eklemek --reverseböylece git rev-listonları oluşturduktan sonra ters sırayla baskılar kaydedilmesini. Bu işe yarar, ancak böyle bir boru hattında kullanırsanız:

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

sadece "id2 yönünde bir sonraki taahhüdü" elde etmek için ve çok uzun bir taahhüt listesi var, git rev-listkomut yazmayı denediğinde headgirdisini okumayı bırakıp çıkmaya çalıştığında kırık bir boru alabilir . Kırık boru hataları normalde kabuk tarafından göz ardı edildiğinden, bu çoğunlukla işe yarar. Emin onlar göz ardı ediyoruz yapmak sizin kullanım.

Ayrıca eklemek cazip gelebilir -n 1için git rev-listbirlikte komuta --reverse. Yapma! Bu, git rev-listbir adım geri yürüdükten sonra durur ve ardından ziyaret edilen taahhütlerin (tek girişli) listesini tersine çevirir. Yani bu <id2>her seferinde ortaya çıkıyor.

Önemli not

"Pırlanta" veya "benzen halkası" grafik parçaları ile:

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

bir taahhüdü "ileri" ye Hdoğru hareket ettirmek yalast size ya I da K . Bu konuda yapabileceğiniz hiçbir şey yok: her iki taahhüt de bir adım ileri! Daha sonra, sonuçta ortaya çıkan işlemden başlayıp başka bir adıma geçerseniz, şimdi hangi yola başladığınızı taahhüt edersiniz.

Bunun tedavisi, her seferinde bir adım hareket etmekten ve yola bağlı zincirlere kilitlenmekten kaçınmaktır. Bunun yerine, başka bir şey yapmadan önce tüm soy yolu zincirini ziyaret etmeyi planlıyorsanız , zincirdeki tüm taahhütlerin tam bir listesini yapın:

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

Ardından, bu listedeki her bir taahhüdü birer birer ziyaret edin ve tüm zinciri alacaksınız. --topo-orderEğer isabet sağlayacağız I-ve- Jbu sırayla ve K-ve- L(KL çifti önce veya sonra IJ çifti yapacağız olmadığını tahmin etmek kolay bir yolu yok olsa) bu sırayla.


" Siz soruyu ekleyene kadar doğru bir cevap yok: özellik yönünde ___ " Bu çok iyi bir nokta. Cevabınız için teşekkürler.
Schwern

4

Bu takma adım var ~/.gitconfig

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


nedir f()? Ve head -1ilk çocuk olması gerekiyor, aksi takdirde bu sadece KAFA rapor edecek.
Xerus

@Xerus Bazı "karmaşık" kabuk sözdizimi kullandığından ve git kaydırılmazsa onu tanımayacaktır f(). Evet, head -1cesur bir tahmindir.
çok zayıf bir

Güzel! nextref = "!f() { git log --reverse --ancestry-path --pretty=%H $1..HEAD | head -${2:-1} | tail -1; }; f"
Benim için tweaked

2

Bir sonraki çocuğu şu şekilde bulmayı başardım:

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

1
benim için bu ilk çocuğu değil, mevcut taahhüdü gösterir
Radon8472

2

Çocuk taahhütlerinin tümü bir dalda ise, gitk --all commit^.."taahhüt" ün taahhütü belirleyen bir şey olduğunu kullanabilirsiniz . Örneğin, komutun kısaltılmış SHA-1'i c6661c5 ise, yazıngitk --all c6661c5^..

Muhtemelen tam SHA-1'i gitk "SHA1 ID:" hücresine girmeniz gerekecektir. Bu örnek için şu adresten elde edilebilecek olan tam SHA-1'e ihtiyacınız olacaktır.git rev-parse c6661c5

Alternatif olarak, git rev-list --all --children | grep '^c6661c5883bb53d400ce160a5897610ecedbdc9d'muhtemelen söz konusu şubenin olup olmadığına bakılmaksızın, bu taahhüdün tüm çocuklarını içeren bir hat üretecektir.


0

Her taahhüt, üst öğeye (birleştirme (standart) taahhüt durumunda ebeveynler) bir işaretçi depolar.

Dolayısıyla, ebeveynten bir çocuk taahhüdüne (varsa) işaret etmenin bir yolu yoktur.


Taahhüt, çocuklarına işaretçiler depolayamaz çünkü daha sonraki noktalara ek çocuk taahhütleri (dallanma noktaları) eklenebilir.
Jakub Narębski

@ Jakub Gerçekten takip etmiyorum. Daha sonra eklenemezler mi?
Schwern

1
Jakub: Tam olarak söylediğim şey bu. 'Her taahhüt, işaretçileri yalnızca ebeveynine depolar.'
Lakshman Prasad

@Schwern: Git'teki taahhütler değişmezdir (bu hesap verebilirliğin güzel bir sonucudur), bu nedenle çocuklara yönelik işaretçiler "daha sonra eklenemez". Sebeplerden biri, taahhüt tanımlayıcısının (örneğin, "üst" bağlantılarda kullanılan), komitenin içeriğine bağlı olmasıdır; merkezi numaralandırma yetkisi olmayan dağıtılmış sistemdeki tek çözüm budur. Ayrıca taahhütlerin "çocukları" sahip olduğunuz dallara bağlıdır ve bu da depodan depoya farklı olabilir (ve taahhütler her depoda aynıdır).
Jakub Narębski

4
@becomingGuru Oy verdim. Doğru olabilir, ama soruma cevap vermiyor. Soru "git'te bir sonraki işi nasıl bulabilirim?" "Git kesinleştirme çocuklarına bir işaretçi depolar mı?"
Schwern


0

Mevcut cevaplar, aradığınız taahhüdü içeren bir şubeniz olduğunu varsayar.

Benim durumumda, aradığım taahhüt, git rev-list --allhiçbir şube içermediğinden yoktu.

gitk --reflogEl ile baktım .

Taahhütünüzü reflogda bile bulamazsanız, aşağıdakilerden birini deneyin:

  • git fsck --full sarkan (yani herhangi bir dalda olmayan) taahhütleri listelemek veya
  • git fsck --lost-found diğer cevaplarda teknikleri uygulamak için sarkan taahhütlere işaret eden refler yapmak.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.