Це не зовсім справжня відповідь, але мені потрібен доступ до форматування та багато місця. Я спробую описати теорію, за якою я вважаю дві найкращі відповіді: прийняту та (принаймні на даний момент) найвищу . Але насправді вони відповідають на різні запитання .
Комітування в Git дуже часто працюють більше ніж на одній гілці одночасно. Справді, саме в цьому полягає питання. Дано:
...--F--G--H <-- master
\
I--J <-- develop
де великі літери стоять на реальні ідентифікатори хеша Git, ми часто шукаємо тільки зробитиH або тільки коммітовI-J у своєму git logвиведенні. Комісії, що Gвиконуються, діють на обох гілках, тому ми хотіли б виключити їх.
(Зверніть увагу , що в графах намальованих як це, нові коммітов в напрямку правого. Імена вибору однієї правою кнопкою найбільш зробити на цій лінії Кожна з цих фіксацій має коммітов батька, який є зробити їх . Зліва: батьківський HIS Gі батько Jє I. батько IISG знову. Батьк Gis F, і Fмає батька, який тут просто не відображається: це частина... розділу.)
Для цього особливо простого випадку ми можемо використовувати:
git log master..develop # note: two dots
бачити I-J , або:
git log develop..master # note: two dots
Hлише для перегляду . Права назва, після двох крапок, говорить Git: так, ці коміти . Назва зліва, перед двома крапками, говорить Git: ні, не ці коміти . Git починається в кінці - при фіксації Hчи фіксації - Jі працює назад . Докладніше про це (набагато) див. У розділі Подумайте, як (а) Git .
Як формулюється оригінальне запитання, прагнення полягає у пошуку комітів, які можна досягти з одного конкретного імені, але не з будь-якого іншого імені в тій самій загальній категорії. Тобто, якщо ми маємо більш складний графік:
O--P <-- name5
/
N <-- name4
/
...--F--G--H--I---M <-- name1
\ /
J-----K <-- name2
\
L <-- name3
ми могли б виділити одне з цих імен, наприклад name4або name3, і запитати: які коміти можна знайти під цим іменем, але не під будь-яким іншим іменем? Якщо ми виберемо name3відповідь, це коміт L. Якщо ми вибираємо name4, то відповідь - взагалі не коміти: коміт, який name4називається комітом, Nале коміт, Nможна знайти, починаючи зname5 та працюючи назад.
Прийнята відповідь працює з іменами віддаленого відстеження, а не з назвами гілок, і дозволяє вам позначити одне - написане origin/merge-only- як вибране ім’я та переглянути всі інші імена в цьому просторі імен. Це також уникає показу злиттів: якщо ми виберемо name1як "цікаву назву" і скажемо показати мені коміти, до яких можна отримати доступ, name1але не будь-яку іншу назву , ми побачимо коммітування злиття M, а також регулярне комітування I.
Найпопулярніша відповідь - зовсім інша. Вся справа в тому, щоб пройти графік коміту, не дотримуючись обох етапів злиття, і не показуючи жодного коміту, що є об’єднанням. name1Наприклад, якщо ми почнемо з , ми не будемо показувати M(це злиття), але якщо припустити, що першим батьківським об'єднанням Mє коміт I, ми навіть не будемо розглядати коміти Jта K. Ми в кінцевому підсумку показуючи фіксації I, а також здійснює H, G, Fі так далі ніхто з них злиття коммітов , і всі вони досяжні, починаючись Mі працювати в зворотному напрямку, відвідуючи тільки перший батько кожного злиття зробити.
Найпопулярніша відповідь досить добре підходить, наприклад, для розгляду того, masterколи masterпередбачається бути гілкою лише для злиття. Якщо вся "справжня робота" була виконана на бічних гілках, які згодом були об'єднані master, ми матимемо такий шаблон:
I---------M---------N <-- master
\ / \ /
o--o--o o--o--o
де всі неіменовані oкоміти - це звичайні (не злиті) комміти та Mі Nє комітами злиття. Фіксація I- це початковий коміт: перший перший коміт, коли-небудь зроблений, і єдиний, який повинен бути на майстрі, який не є комітом об’єднання. Якщо git log --first-parent --no-merges masterшоу показує будь-який комміт, крім I , ми маємо таку ситуацію:
I---------M----*----N <-- master
\ / \ /
o--o--o o--o--o
де ми хочемо бачити коміт * який було зроблено безпосередньо master, а не шляхом об’єднання якоїсь гілки функцій.
Коротше кажучи, популярна відповідь чудова для того, щоб подивитися, masterколиmaster призначено лише для злиття, але не така чудова для інших ситуацій. Прийнята відповідь працює для цих інших ситуацій.
Це імена віддаленого відстеження, такі як origin/master галузь назв ?
Деякі частини Git кажуть, що це не так:
git checkout master
...
git status
говорить on branch master, але:
git checkout origin/master
...
git status
каже HEAD detached at origin/master. Я вважаю за краще погодитися з git checkout/ git switch: origin/master- це не назва гілки, тому що ви не можете отримати "на" це.
Прийнята відповідь використовує імена віддаленого відстеження origin/*як "назви гілок":
git log --no-merges origin/merge-only \
--not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
grep -Fv refs/remotes/origin/merge-only)
Середній рядок, який викликається git for-each-ref, переглядає імена віддаленого відстеження для імені віддаленогоorigin .
Причиною того, що це хороше рішення вихідної проблеми, є те, що нас тут цікавлять чужі імена філій, а не імена наших філій. Але це означає, що ми визначили галузь як щось інше, ніж імена наших філій . Це добре: просто пам’ятайте, що ви робите це, коли це робите.
git log перетинає деякі частини графіка коміту
Те, що ми насправді шукаємо тут, - це серія того, що я назвав даглетами: дивіться, що саме ми маємо на увазі під словом "гілка"? Тобто ми шукаємо фрагменти в деякій підмножині загального графу комітів .
Щоразу, коли ми Git дивимось на назву гілки master, як тег, як v2.1, або на ім'я віддаленого відстеження origin/master, ми, як правило, хочемо, щоб Git розповідав нам про цей коміт і кожен коміт, до якого ми можемо дістатись із цього коміту: починаючи з цього , і працює назад.
У математиці це називають розмахуванням графіка . Графік коміту Git - це спрямований ациклічний графік або DAG , і такий графік особливо підходить для ходьби. Під час прогулянки таким графіком відвідувач відвідує кожну вершину графіка, до якої можна дістатися через використовуваний шлях. Вершини в графіку Git - це коміти, причому краї являють собою дуги - односторонні посилання - ідуть від кожної дочірньої до кожного з батьків. (Це де Think Like (a) Git . Односторонність дуг означає, що Git повинен працювати назад, від дитини до батьків).
Дві основні команди Git для прогулянки по графіку - це git logі git rev-list. Ці команди надзвичайно схожі - насправді вони в основному побудовані з одних і тих самих вихідних файлів, - але їх вихід відрізняється: git logвидає вихідні дані для читання людьми, тоді як git rev-listвидає вихідні дані, призначені для читання інших програм Git. 1 Обидві команди виконують такий тип переходу по графіку.
Графічна прогулянка, яку вони роблять, це конкретно: з урахуванням певного набору комітів початкової точки (можливо, лише одного коміту, можливо, купи хеш-ідентифікаторів, можливо, групи імен, які вирішують хеш-ідентифікатори), пройдіться графіком, відвідуючи коміти . Конкретні директиви, такі як --notабо префікс ^, або --ancestry-path, або --first-parent, якимось чином змінюють графічну прогулянку .
Коли вони роблять графічну прогулянку, вони відвідують кожну коміту. Але вони друкують лише деяку вибрану підмножину пройдених комітів. Директиви, такі як --no-mergesабо --before <date>повідомляють графічний код, який зобов'язує друкувати .
Для цього відвідування, по одному коміту за раз, ці дві команди використовують пріоритетну чергу . Ви запускаєте git logабо git rev-listдаєте йому певні початкові пункти комітів. Вони поміщають ці коміти до черги пріоритетів. Наприклад, простий:
git log master
перетворює ім'я masterна необроблений хеш-ідентифікатор і поміщає цей один хеш-ідентифікатор у чергу. Або:
git log master develop
перетворює обидва імена в хеш-ідентифікатори і - припускаючи, що це два різні хеш-ідентифікатори - поміщає обидва в чергу.
Пріоритет комітів у цій черзі визначається ще більшою кількістю аргументів. Наприклад, аргумент --author-date-orderповідомляє git logабо git rev-listвикористовувати позначку часу автора , а не мітку часу комітера. За замовчуванням використовується позначка часу комітера та вибір найновішого за датою коміту: того, що має найвищу числову дату. Отже master develop, припускаючи, що ці рішення вирішуються на два різних коміти, Git покаже, який із них відбувся пізніше , тому що це буде в передній частині черги.
У будь-якому випадку код перегляду редакції тепер працює у циклі:
- Поки в черзі є коміти:
- Видаліть перший запис черги.
- Вирішіть, чи друкувати цей коміт взагалі. Наприклад
--no-merges: нічого не надрукуйте, якщо це коміт злиття; --before: нічого не друкувати, якщо його дата настає не раніше визначеного часу. Якщо друк не припинено, надрукуйте коміт: for git log, покажіть його журнал; для git rev-list, надрукуйте його хеш-ідентифікатор.
- Помістіть деякі або всі батьківські коміти цього коміту в чергу (до тих пір, поки його зараз немає і його ще не відвідували 2 ). Звичайним за замовчуванням є введення всіх батьків. Використання
--first-parentпридушує всіх батьків, крім першого, кожного злиття.
(І те, git logі інше git rev-listможе спростити історію з переписуванням батьків чи без нього на цьому етапі, але ми пропустимо це тут.)
Для простого ланцюжка, наприклад, почати з HEADі працювати назад, коли немає комітів злиття, у черзі завжди є одне комітування вгорі циклу. Існує одне комітування, тому ми виймаємо його і друкуємо, і ставимо його (одного) батька в чергу і знову обходимо, і ми йдемо по ланцюжку назад, поки не дійдемо до самого першого коміту, або користувач не втомиться від git logвиводу і не завершить роботу Програма. У цьому випадку жоден з варіантів впорядкування не має значення: є лише одне зобов'язання, яке потрібно показати.
Коли відбувається злиття, і ми слідуємо за обома батьками - обома "ногами" злиття - або коли ви даєте git logабоgit rev-list декілька початкових комітів, варіанти сортування мають значення.
Нарешті, розглянемо ефект специфікатора коміту --notабо ^перед ним. Їх можна записати кількома способами:
git log master --not develop
або:
git log ^develop master
або:
git log develop..master
всі означають одне і те ж. Це --notяк префікс, ^за винятком того, що він застосовується до кількох імен:
git log ^branch1 ^branch2 branch3
означає не гілка1, не гілка2, та гілка3; але:
git log --not branch1 branch2 branch3
означає не галузь1, не гілка2, не гілка3, і вам потрібно використати секунду, --notщоб її вимкнути:
git log --not branch1 branch2 --not branch3
що трохи незручно. Дві директиви "не" поєднуються через XOR, тому, якщо ви дійсно хочете, ви можете написати:
git log --not branch1 branch2 ^branch3
мати у вигляді , НЕ branch1, а НЕ branch2, так branch3 , якщо ви хочете Затемнення .
Всі вони працюють, впливаючи на графічну прогулянку. Як git logі git rev-listходить граф, він переконується НЕ ставити в пріоритет черги будь Комміт , який доступний з будь-якого з заперечень посилань. (Насправді вони також впливають на початкове налаштування: відмінені коміти не можуть потрапити до черги пріоритетів прямо з командного рядка, тому git log master ^master, наприклад, нічого не відображається.)
Весь вигадливий синтаксис, описаний у документації gitrevisions, використовує це, і ви можете розкрити це простим викликом git rev-parse. Наприклад:
$ git rev-parse origin/pu...origin/master # note: three dots
b34789c0b0d3b137f0bb516b417bd8d75e0cb306
fc307aa3771ece59e174157510c6db6f0d4b40ec
^b34789c0b0d3b137f0bb516b417bd8d75e0cb306
Синтаксис із трьома крапками означає коміти, досяжні з лівої або правої сторони, але виключаючи коміти, доступні з обох . В цьому випадку origin/masterздійснював b34789c0b, сам по собі досяжному з origin/pu( fc307aa37...) , так що origin/masterз'являється хеш двічі, один раз з запереченням, але насправді Git досягає трьох-точковий синтаксис, поставивши в двох позитивних посиланнях : зо два не-заперечує хеш - ідентифікаторів, і один негативний, представлений ^префіксом.
Подібно:
$ git rev-parse master^^@
2c42fb76531f4565b5434e46102e6d85a0861738
2f0a093dd640e0dad0b261dae2427f2541b5426c
В ^@означає синтаксичний всі батьки даного зобов'язання , і master^сам-перший батько Комміт вибрано відгалуження ім'я master-пов злиття фіксацію, тому він має два батьків. Це двоє батьків. І:
$ git rev-parse master^^!
0b07eecf6ed9334f09d6624732a4af2da03e38eb
^2c42fb76531f4565b5434e46102e6d85a0861738
^2f0a093dd640e0dad0b261dae2427f2541b5426c
В ^!означає суфікс НЕ взяти на себе зобов'язання, але жоден з його батьків . У цьому випадку master^є 0b07eecf6.... Ми вже бачили обох батьків із ^@суфіксом; ось вони знову, але цього разу заперечені.
1 Багато програм Git буквально запускаються git rev-listз різними опціями та читають їх вихідні дані, щоб знати, які коміти та / або інші об'єкти Git використовувати.
2 Оскільки графік ациклічний , можна гарантувати, що жодного з них вже не відвідували, якщо ми додамо обмеження, ніколи не показувати батьків, перш ніж показувати всі його дочірні пріоритети. --date-order, --author-date-orderІ --topo-orderдодати це обмеження. Порядок сортування за замовчуванням - який не має імені - ні. Якщо мітки часу фіксації є хибними - якщо, наприклад, деякі коміти були зроблені "в майбутньому" комп'ютером, у якого годинник був вимкнений, - це в деяких випадках може призвести до дивного виводу.
Якщо ви зайшли так далеко, то тепер знаєте багато про що git log
Короткий зміст:
git log стосується показу деяких вибраних комітів під час прогулянки по деякій або всій частині частини графіку.
--no-mergesАргумент, знайдений в обох прийнятих і в даний час, топ-рейтинг відповідей, пригнічує показуючи деякі коммітов , які є йшли.
--first-parentАргумент, з поточної-топ-рейтинг-відповідь, пригнічує ходьбу деякі частини графіка, в протягом граф-ходити сам.
--notПрефікс аргументів командного рядка, який використовується в загальноприйнятому відповідь, пригнічує коли - або відвідувати деякі частини графіка взагалі з самого початку.
Ми отримуємо відповіді, які нам подобаються, на два різні запитання, використовуючи ці функції.
git log ^branch1 ^branch2 merge-only-branchсинтаксис?