Як я можу знайти наступний комікт у git? (дитина / діти відліку)


236

ref^ відноситься до скоєного раніше ref , а як бути після здійснення комітетуref ?

Наприклад, якщо я, git checkout 12345як я можу перевірити наступну комісію?

Дякую.

PS Так, git - це дерево структури вказівника DAG на будь-якому місці. Як я можу знайти команду після цього?



2
Дивіться також " git children-of"!
VonC

Відповіді:


185

Щоб перерахувати всі коміти, починаючи з поточного, а потім його дочірнього тощо, - в основному стандартний журнал git, але йдучи іншим часом, скористайтеся чимось на зразок

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

де 894e8b4e93d8f3 - це перший виклик, який ви хочете показати.


3
Для конкретного випадку в первісному питанні, просто замінити HEAD^на 894e8b4e93d8f3^.
Søren Løvborg

2
Можливо, додайте --oneline - кращий короткий результат виводу.
фіро

1
Мені потрібно користуватися ..., ^..не вдається мовчки
TankorSmash

1
Початок fatal: unrecognized argument: --ancestry-pathу версії git 1.7.1
користувач151841

6
Це буде спрацьовувати лише у випадку, якщо він masterстоїть на батьківському шляху поточного комітету. Дивіться другий фрагмент коду моєї відповіді щодо рішення, яке буде працювати у всіх випадках.
Том Хейл

37

Творець для Хадсона (тепер Дженкінс) Кохсуке Кавагучі щойно опублікував (листопад 2013 р.):
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 (спрямований ациклічний графік) , немає "одного батька" чи "однієї дитини".

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

Впорядкування комісій здійснюється "топо-замовленням" або "датою-замовленням" (див. Книгу GitPro )

Але оскільки 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: Це правда, git rev-list --childrenце не для того, щоб перелічити лише дітей, а для того, щоб перерахувати батьків з їхніми дітьми ... завжди потрібно розібратися.
VonC

Я спробував цей код на комітеті з двома дітьми: $ git children0of 9dd5932 fatal: No annotated tags can describe '71d7b5dd89d241072a0a078ff2c7dfec05d52e1f'. However, there were unannotated tags: try --tags. який вихід ви отримуєте?
Том Хейл,

@TomHale Я не можу перевірити його зараз, але задайте нове запитання (з ОС та Git версіями), таким чином кожен може перевірити.
VonC

1
Єдина проблема git-children-ofполягає в тому, що він використовує git опис, який намагається відформатувати SHA як читабельний для людини, що може не вдатися до помилки @ TomHale, і дає такі результати v1.0.4-14-g2414721заплутані, якщо ви очікували SHA. Заміна його на простий echoробить це відмінним інструментом, дякую!
Миколай

18

що я знайшов - це

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

де я встановлений commit1як поточний фіксатор та commit2поточний керівник. Це повертає мені список усіх комітетів, які будують шлях між commit1іcommit2 .

Останній рядок виводу - дочірня частина1


3
Тому просто додайте, | tail -1щоб отримати дитину.
Джессі Глік

8

Я знаю, що ти маєш на увазі. Прикро мати ситний синтаксис для переходу до попередніх комісій, але жоден не переходити до наступних. У складній історії проблема "що є наступним прихильником" стає досить важкою, але потім у складному злитті виникає така ж твердість і з "попередніми" зобов'язаннями. У простому випадку, всередині однієї гілки з лінійною історією (навіть лише локально для деякої обмеженої кількості комітетів) було б непогано і було б сенсом йти вперед і назад.

Справжня проблема з цим полягає в тому, що діти, які вчиняються, не мають посилань, це лише список, пов'язаний із зворотним зв'язком. Знаходження дитини здійснює пошук, який не дуже поганий, але, ймовірно, не те, що git хоче вкласти в логіку refspec.

У будь-якому випадку я поставив це питання, тому що просто хочу зробити крок вперед по одній історії, роблячи тести, а іноді доводиться крокувати вперед, а не назад. Ну, ще з деякою думкою я придумав таке рішення:

Виберіть зобов’язання перед тим, де ви знаходитесь. Можливо, це може бути керівник відділення. Якщо ви знаходитесь у відділенні ~ 10, тоді "git checkout branch ~ 9", то "git checkout branch ~ 8", щоб отримати наступне після цього, тоді "git checkout branch ~ 7" тощо.

Зменшення числа повинно бути дуже простим у сценарії, якщо воно вам потрібно. Набагато простіше, ніж розбір git rev-list.


Незважаючи на те, що вона корисна, вона не знаходить наступного комітету. Він іде до поточного комітету.
Шверн

1
Добре, тоді я гадаю, що ти міг би зробити: " BRANCH=master; git co $BRANCH~$[ $(git rev-list HEAD..$BRANCH | wc -l) - 1 ]" Ви повинні йти до гілки, не обходячи це.
vontrapp

8

Дві практичні відповіді:

Одна дитина

На основі відповіді @ Майкла , я зламав 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якщо ви хочете коротку хеш-форму.

Кілька дітей

З відокремленою ГОЛОВОЮ (немає відділення) або дістати всіх дітей незалежно від гілок:

# 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
За цією логікою немає і "попередньої фіксації", але є багато синтаксису для отримання батьків (ів).
Шверн

7
@Schwern: "Немає" попередньої фіксації "; <rev>^є "батьківська комісія" ("перший батьків" для об'єднань).
Якуб Нарубський

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}на деякий посилання (гілка, тег, sha1) комітету, діти якого вас цікавлять.


2
це відповідає на питання, яке я не знав, як сформулювати для google та stackoverflow, але намагався задати. дякую за те, що активно визнали потребу
Томмі Ноултон

для мене ${COMMIT}не чинить esist, але ви можете використовувати $(git rev-parse HEAD) замість цього
Radon8472

вибачте, додав примітку о ${COMMIT}.
Метт Макенрі

5

(Це почалося як відповідь на повторне запитання. Я трохи проробив редагування, щоб очистити його.)

Усі внутрішні стрілки Git є односторонніми, спрямовані назад. Тому немає короткого зручного синтаксису для руху вперед: це просто неможливо.

Це є можливим «рухатися проти стріл», але спосіб зробити це дивно , якщо ви ще не бачили його раніше, і потім очевидно після цього. Скажімо, у нас є:

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

Використання middle~2слідує стрілками двічі від Cспини до A. Отже, як ми переходимо Cдо D? Відповідь: ми починаємо E, використовуючи ім'я last, і працюємо назад, поки не дістаємось middle, записуючи пункти, які ми відвідуємо по дорозі . Тоді ми просто рухаємось, наскільки хочемо, у напрямку last: рухаємось на один крок Dабо два наE .

Це особливо важливо, коли у нас є філії:

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

Яка фіксація є кроком після C? Правильної відповіді немає, поки ви не додасте до запитання: у напрямку особливості___ (заповніть пусту).

Для перерахування комітетів між 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, Gі I, і, Fі Hв певному порядку. З --ancestry-pathвибивають Hі I: вони не є нащадками C, тільки A. З --topo-orderми впевнені , що фактичний порядок перерахування є J, то G, то F.

git rev-listКоманда розливає ці ідентифікатори хеш на свій стандартний висновок, по одному в рядку. Щоб рухатись на крок вперед в напрямку 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команди разом з --reverse. Не робіть цього! Це git rev-listзупиняється після того, як піти на крок назад , а потім перевернути (одноразовий) список відвідуваних комітетів. Тож це <id2>щоразу виробляється .

Важлива бічна примітка

Зауважте, що з фрагментами графіка "алмаз" або "бензольне кільце":

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

переміщаючи один робить «вперед» від в Hсторону lastотримає Вас або I або K . З цим нічого не можна зробити: обидва дії - це крок вперед! Якщо ви почнете з отриманого комітету і перейдете до іншого кроку, тепер ви покладете на той шлях, яким ви почали.

Вирішення цього полягає в тому, щоб уникнути переміщення покроково і не замикатися на ланцюгах, залежних від шляху. Натомість, якщо ви плануєте відвідати цілий ланцюжок породних шляхів, перш ніж робити щось інше , складіть повний перелік усіх зобов'язань у ланцюжку:

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

Потім відвідайте кожну комісію в цьому списку, по одному, і ви отримаєте всю ланцюжок. --topo-orderБуде переконатися , що ви потрапили I-і- Jв такому порядку, і K-і- Lв такому порядку (хоча немає ніякого простого способу передбачити , чи будете ви зробити пару IJ до або після пари KL).


" Немає правильної відповіді, поки ви не додасте до питання: у напрямку особливості___ " Це дуже хороший момент. Дякую за вашу відповідь.
Schwern

4

У мене є цей псевдонім у ~/.gitconfig

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


що таке f()? І потрібно бути head -1першою дитиною, інакше це просто повідомить ГОЛОВУ.
Ксерус

@Xerus Оскільки він використовує якийсь "складний" синтаксис оболонки, і git не розпізнає його, якщо не буде завершено f(). Так, head -1це смілива здогадка.
слабкий

Приємно! Підірвав міну: nextref = "!f() { git log --reverse --ancestry-path --pretty=%H $1..HEAD | head -${2:-1} | tail -1; }; f"щоб ви могли вибрати, як далеко вперед, за бажанням
З. Хулла

2

Мені вдалося знайти наступну дитину наступним чином:

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

1
для мене це показує не першу дитину, це показує нинішній
вчинок

2

Якщо дитина здійснює всі зобов’язання на якійсь гілці, ви можете використовувати gitk --all commit^.., де "фіксувати" - це щось, що ідентифікує виконання. Наприклад, якщо скорочено SHA-1 комітету є c6661c5, тоді введітьgitk --all c6661c5^..

Ймовірно, вам потрібно буде ввести повний SHA-1 в клітинку "SHA1 ID:" gitk. Вам знадобиться повний SHA-1, який для цього прикладу можна отримати за допомогоюgit rev-parse c6661c5

Крім того, git rev-list --all --children | grep '^c6661c5883bb53d400ce160a5897610ecedbdc9d'буде створено рядок, що містить усіх дітей цього комітету, імовірно, є чи ні філія.


0

Кожна комісія зберігає вказівник до свого батьківського (батьків, у випадку злиття (стандартного) прихильності).

Отже, немає жодного способу вказати на вчинення дитиною (якщо така є) батьків.


Комітет не може зберігати вказівники для своїх дітей, оскільки додаткові дочірні коміти (точки розгалуження) можуть бути додані в пізній момент.
Якуб Нарубський

@Jakub Я насправді не дотримуюся. Їх не можна додати пізніше?
Шверн

1
Якуб: Саме це я і сказав. "Кожна комісія зберігає вказівники лише для свого батьківського".
Лакшман Прасад

@Schwern: Коміти в git незмінні (що має приємні наслідки підзвітності), тому вказівки для дітей не можуть "бути додані пізніше". Однією з причин є те, що ідентифікатор фіксування (використовується, наприклад, у "батьківських" посиланнях) залежить від вмісту комітету; це єдине рішення в розподіленій системі без центрального органу нумерації. Також "діти" комітетів залежать від ваших гілок, і це, в свою чергу, може відрізнятися від сховища до сховища (а коміти однакові у кожному сховищі).
Якуб Нарбський

4
@becomingGuru Я проголосував за це. Це може бути правдою, але це не відповідає на моє запитання. Питання полягає в тому, "як я можу знайти наступний фікс в git?" Це не "чи git commit зберігає вказівник для своїх дітей?"
Шверн

0

Ця публікація ( http://www.jayway.com/2015/03/30/using-git-commits-to-drive-a-live-coding-session/#comment-282667 ) показує акуратний спосіб, якщо це зробити, якщо ви можете створити чітко визначений тег у кінці стеку фіксування. По суті, git config --global alias.next '!git checkout `git rev-list HEAD..demo-end | tail -1`' де "demo-end" є останнім тегом.


0

Існуючі відповіді передбачають, що у вас є відділення, яке містить комісію, яку ви шукаєте.

У моєму випадку зобов’язання, яке я шукав, не було, git rev-list --allоскільки жодна галузь не містила цього.

Я переглянув gitk --reflogвручну.

Якщо ви не можете знайти свої зобов’язання навіть у запиті, спробуйте:

  • git fsck --full перерахувати звисаючі (тобто не в жодній галузі) комісії, або
  • git fsck --lost-found робити рефлекси, що вказують на звисаючі зобов’язання, застосовувати методи в інших відповідях.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.