Git log, чтобы получить коммиты только для конкретной ветки

214

Я хочу перечислить все коммиты, которые являются только частью определенной ветви.

С учетом следующего, он перечисляет все коммиты из ветви, но также из родительского (мастер)

git log mybranch

Другой вариант, который я нашел, состоял в том, чтобы исключить коммиты, доступные мастеру, и дает мне то, что я хочу, НО я хотел бы избежать необходимости знать имена других ветвей.

git log mybranch --not master

Я пытался использовать git for-each-ref, но он также перечисляет mybranch, так что на самом деле он исключает все:

git log mybranch --not $(git for-each-ref --format '^%(refname:short)' refs/heads/)

Обновить:

Я тестирую новый вариант, который я нашел некоторое время назад, и до сих пор кажется, что это то, что я искал:

git log --walk-reflogs mybranch

Обновление (2013-02-13T15: 08):

Опция --walk-reflogs хороша, но я проверил, что для reflogs истек срок действия (по умолчанию 90 дней, gc.reflogExpire ).

Я думаю, что нашел ответ, который искал:

git log mybranch --not $(git for-each-ref --format='%(refname)' refs/heads/ | grep -v "refs/heads/mybranch")

Я просто удаляю текущую ветку из списка доступных ветвей и использую этот список для исключения из журнала. Таким образом, я получаю только те коммиты, которые доступны только моему филиалу .

dimirc
источник
возможно дублирование с stackoverflow.com/questions/53569
StarPinkER
Уже видел этот вопрос также, но не тот же самый
dimirc
2
Вы уверены, что это то, что вы хотите? Я считаю, что лучше иметь в виду цель: «что на моей ветке не в верхнем потоке», или «что на моей ветке, а не в основной». Кроме того, в то время как git быстро отбирает, это будет дороже, так как у вас будет больше веток. У меня есть скрипт, который я использую, который я называю «git отсутствующий» после команды bzr «отсутствует». Вы можете найти его здесь: github.com/jszakmeister/etc/blob/master/git-addons/git-missing .
Джон Szakmeister
1
Я действительно нуждаюсь в этом для ловушки после получения, так что "master" не всегда будет исключать ветвь
dimirc
Да, дубликат, упомянутый StarPinkER, хорошо работал для меня: git log $ (ветка git merge-base HEAD) .. ветвь
charo

Ответы:

165

Из того, как это звучит, вы должны использовать cherry:

git cherry -v develop mybranch

Это покажет все коммиты, которые содержатся в mybranch , но НЕ в разработке . Если вы пропустите последний вариант ( mybranch ), он будет сравнивать текущую ветку.

Как указал VonC, вы ВСЕГДА сравниваете свою ветку с другой веткой, поэтому знайте свои ветви, а затем выбирайте, с какой из них сравнивать.

Смайлик
источник
4
Это именно то, что мне нужно, но, похоже, для этого должна быть более интуитивная команда (да, даже в мире Git).
Сет
12
Вы также git cherry -v masterможете сравнить текущую ветку с главной веткой.
Питикос
2
Опасность использования git cherry заключается в том, что коммиты сопоставляются только в том случае, если их различия в файлах идентичны между ветвями. Если было выполнено какое-либо слияние, которое делало бы различия между одной веткой и другой разницей, то git cherry рассматривает их как разные коммиты.
Бен
Это просто дает мне fatal: Unknown commit mybranch.
Мэтт Арнольд
1
@MattArnold, вы должны изменить текст «развернуть» и «mybranch» на ветки, которые существуют в вашем репо
Smilie
40

НО я хотел бы избежать необходимости знать имена других ветвей.

Я не думаю, что это возможно: ветка в Git всегда основана на другом или, по крайней мере, на другом коммите, как объяснено в « git diff не показывает достаточно »:

введите описание изображения здесь

Вам нужен ориентир для вашего журнала, чтобы показать правильные коммиты.

Как упомянуто в « GIT - Откуда я ветвился? »:

ветви - это просто указатели на определенные коммиты в DAG

Поэтому, даже если git log master..mybranchответ один, он все равно будет показывать слишком много коммитов, если mybranchон основан на myotherbranchсамомmaster .

Чтобы найти эту ссылку (источник вашей ветви), вы можете только проанализировать коммиты и посмотреть, в какой ветви они находятся, как показано в:

VonC
источник
26

Я наконец нашел способ сделать то, что хотел ОП. Это так просто, как:

git log --graph [branchname]

Команда отобразит все коммиты, которые доступны из предоставленной ветки в формате графика. Но вы можете легко отфильтровать все коммиты в этой ветке, посмотрев на граф коммитов,* коммитов является первым символом в строке коммитов.

Например, давайте посмотрим на выдержку из git log --graph masterрепозитория GitHub на cakephp:

D:\Web Folder\cakephp>git log --graph master
*   commit 8314c2ff833280bbc7102cb6d4fcf62240cd3ac4
|\  Merge: c3f45e8 0459a35
| | Author: José Lorenzo Rodríguez <lorenzo@users.noreply.github.com>
| | Date:   Tue Aug 30 08:01:59 2016 +0200
| |
| |     Merge pull request #9367 from cakephp/fewer-allocations
| |
| |     Do fewer allocations for simple default values.
| |
| * commit 0459a35689fec80bd8dca41e31d244a126d9e15e
| | Author: Mark Story <mark@mark-story.com>
| | Date:   Mon Aug 29 22:21:16 2016 -0400
| |
| |     The action should only be defaulted when there are no patterns
| |
| |     Only default the action name when there is no default & no pattern
| |     defined.
| |
| * commit 80c123b9dbd1c1b3301ec1270adc6c07824aeb5c
| | Author: Mark Story <mark@mark-story.com>
| | Date:   Sun Aug 28 22:35:20 2016 -0400
| |
| |     Do fewer allocations for simple default values.
| |
| |     Don't allocate arrays when we are only assigning a single array key
| |     value.
| |
* |   commit c3f45e811e4b49fe27624b57c3eb8f4721a4323b
|\ \  Merge: 10e5734 43178fd
| |/  Author: Mark Story <mark@mark-story.com>
|/|   Date:   Mon Aug 29 22:15:30 2016 -0400
| |
| |       Merge pull request #9322 from cakephp/add-email-assertions
| |
| |       Add email assertions trait
| |
| * commit 43178fd55d7ef9a42706279fa275bb783063cf34
| | Author: Jad Bitar <jadbitar@mac.com>
| | Date:   Mon Aug 29 17:43:29 2016 -0400
| |
| |     Fix `@since` in new files docblocks
| |

Как вы можете видеть, только фиксации 8314c2ff833280bbc7102cb6d4fcf62240cd3ac4и c3f45e811e4b49fe27624b57c3eb8f4721a4323bиметь *существо первого символ фиксация линии. Эти коммиты из основной ветки, а остальные четыре из других веток.

Лукман
источник
11

Следующая команда оболочки должна делать то, что вы хотите:

git log --all --not $(git rev-list --no-walk --exclude=refs/heads/mybranch --all)

Предостережения

Если вы mybranchпроверили, приведенная выше команда не будет работать. Это потому, что коммиты mybranchтакже доступны HEAD, поэтому Git не считает коммиты уникальными для них mybranch. Чтобы заставить его работать, когда mybranchон выписан, вы также должны добавить исключение для HEAD:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=HEAD \
    --all)

Тем не менее, вы не должны исключать, HEADпока mybranchне извлечены, в противном случае вы рискуете показать коммиты, которые не являются исключительными mybranch.

Точно так же, если у вас есть удаленная ветка с именем, origin/mybranchкоторая соответствует локальной mybranchветке, вам придется исключить ее:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=refs/remotes/origin/mybranch \
    --all)

И если удаленная ветвь является веткой по умолчанию для удаленного репозитория (обычно только для true origin/master), вам также придется исключить origin/HEAD:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=refs/remotes/origin/mybranch \
    --exclude=refs/remotes/origin/HEAD \
    --all)

Если у вас есть разветвленная ветвь, и есть удаленная ветвь, и удаленная ветвь является удаленной веткой по умолчанию, то в итоге вы исключаете много:

git log --all --not $(git rev-list --no-walk \
    --exclude=refs/heads/mybranch \
    --exclude=HEAD
    --exclude=refs/remotes/origin/mybranch \
    --exclude=refs/remotes/origin/HEAD \
    --all)

объяснение

Эта git rev-listкоманда представляет собой низкоуровневую (сантехническую) команду, которая обходит указанные ревизии и выводит встреченные идентификаторы SHA1. Думайте об этом как эквивалентgit log исключением того, что он показывает только SHA1 - без сообщения журнала, без имени автора, без отметки времени, ничего из этого "причудливого" материала.

--no-walkВариант, как следует из названия, предотвращает git rev-listот ходьбы родословной цепи. Так что, если вы введете git rev-list --no-walk mybranchего, он напечатает только один идентификатор SHA1: идентификатор коммита подсказкиmybranch ветви.

В --exclude=refs/heads/mybranch --allаргументах говорят , git rev-listчтобы начать с каждой ссылки , за исключениемrefs/heads/mybranch .

Таким образом, когда вы запускаете git rev-list --no-walk --exclude=refs/heads/mybranch --all, Git печатает идентификатор SHA1 коммитов чаевых для каждой ссылки, кроме refs/heads/mybranch. Эти коммиты и их предки - это коммиты, которые вам не интересны - это коммиты, которые вам не нужны хотите видеть.

В других фиксаций являются те , которые вы хотите видеть, поэтому мы собираем вывод git rev-list --no-walk --exclude=refs/heads/mybranch --allи сказать Git , чтобы показать все , кроме тех фиксаций и их предки.

--no-walkАргумент необходим для больших хранилищ (и является оптимизацией для небольших хранилищ): Без него, Git бы печатать, и оболочке придется собирать (и хранить в памяти) много больше совершают идентификаторы , чем это необходимо. В большом хранилище количество собранных коммитов может легко превысить предел аргументов командной строки оболочки.

Git ошибка?

Я ожидал бы следующее, чтобы работать:

git log --all --not --exclude=refs/heads/mybranch --all

но это не так. Я предполагаю, что это ошибка в Git, но, возможно, это намеренно.

Ричард Хансен
источник
Хорошее объяснение. +1
VonC
5
Я пришел сюда, желая знать, как делать Меркуриал hg log -b <branch>. Я не понимаю, почему люди говорят, что мерзавец недружелюбен. / s
weberc2
Я не знаю, почему git log --all --not --exclude=refs/heads/mybranch --allне работает, но git log refs/heads/mybranch --not --exclude=refs/heads/mybranch --allработает, с теми же предостережениями об исключении HEAD и происхождения.
Бен С
8

Быстрый ответ:

git log $(git merge-base master b2)..HEAD

Скажем так:

  1. Что у вас есть мастер ветка

  2. Сделать несколько коммитов

  3. Вы создали ветку с именем b2

  4. Делать git log -n1; идентификатор фиксации - это база слияния между b2 и master

  5. Сделать несколько коммитов в b2

  6. git log покажет ваш журнал истории b2 и мастер

  7. Используйте диапазон фиксации, если вы не знакомы с концепцией, я приглашаю вас погуглить или переполнить ее,

    Для вашего реального контекста, вы можете сделать, например,

    git log commitID_FOO..comitID_BAR
    

    «..» - оператор диапазона для команды log.

    Это означает, что в простой форме, дайте мне все журналы, более свежие, чем commitID_FOO ...

  8. Посмотрите на пункт № 4, база слияния

    Итак: git log COMMITID_mergeBASE..HEADпокажу вам разницу

  9. Git может получить базу слияния для вас, как это

    git merge-base b2 master
    
  10. Наконец вы можете сделать:

    git log $(git merge-base master b2)..HEAD
    
Фредерик Нот
источник
4

Вы можете попробовать что-то вроде этого:

#!/bin/bash

all_but()
{
    target="$(git rev-parse $1)"
    echo "$target --not"
    git for-each-ref --shell --format="ref=%(refname)" refs/heads | \
    while read entry
    do
        eval "$entry"

        test "$ref" != "$target" && echo "$ref"
    done
}

git log $(all_but $1)

Или заимствуя рецепт из Руководства пользователя Git :

#!/bin/bash
git log $1 --not $( git show-ref --heads | cut -d' ' -f2 | grep -v "^$1" )
Джон Шакмейстер
источник
+1. Я писал в своем ответе, что вам нужно проанализировать коммиты, чтобы найти источник вашей ветки, и вы, кажется, сделали именно это.
VonC
Вы использовали log --walk-reflogs? Я читаю об этой опции и дает мне результаты, которые мне нужны до сих пор (все еще тестирую)
dimirc
@dimirc Reflogs - все это разные звери. Он записывает точки ветвления с течением времени, как правило, с целью восстановления. Я не уверен, что вы делаете в своем пост-получении, но, возможно, если бы вы объяснили это, люди могли бы дать более разумный ответ на вашу проблему.
Джон Szakmeister
Мне просто нужно проанализировать / проверить все сообщения коммитов одним нажатием. Случай, который я хочу осветить, - это когда кто-то выдвигает несколько коммитов в master и создает новую ветку с несколькими коммитами. Хук post-receive должен проверять изменения, сделанные для обеих ссылок / веток, например master и newbranch, мне нужно знать границы для новой ветки, чтобы избежать синтаксического анализа сообщений коммита дважды, так как это может быть зафиксировано коммитом от master.
Димирк
1
Ладно. Тогда я думаю, что изложенный выше подход действительно то, что вы хотите. С reflog, записи будут падать через некоторое время, и я думаю, что это вызовет некоторые головные боли. Вы можете посмотреть на использование git show-ref --tagsтоже.
Джон Szakmeister
4
git rev-list --exclude=master --branches --no-walk

будет перечислять советы каждой отрасли, которая не master.

git rev-list master --not $(git rev-list --exclude=master --branches --no-walk)

перечислит каждый коммит в masterистории России, которого нет в истории любого другого филиала.

Последовательность важна для опций, которые устанавливают конвейер фильтра для выбора коммита, поэтому --branchesдолжна следовать всем шаблонам исключения, которые она должна применять, и --no-walkдолжна следовать фильтрам, предоставляющим коммиты. Rev-list не должен проходить.

jthill
источник
4

Это выведет коммиты на текущую ветку. Если какой-либо аргумент передается, он просто выводит хэши.

git_show_all_commits_only_on_this_branch

#!/bin/bash
function show_help()
{
  ME=$(basename $0)
  IT=$(cat <<EOF
  
  usage: $ME {NEWER_BRANCH} {OLDER_BRANCH} {VERBOSE}
  
  Compares 2 different branches, and lists the commits found only 
  in the first branch (newest branch). 

  e.g. 
  
  $ME         -> default. compares current branch to master
  $ME B1      -> compares branch B1 to master
  $ME B1 B2   -> compares branch B1 to B2
  $ME B1 B2 V -> compares branch B1 to B2, and displays commit messages
  
  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

# Show commit msgs if any arg passed for arg 3
if [ "$3" ]
then
  OPT="-v"
fi

# get branch names
OLDER_BRANCH=${2:-"master"}
if [ -z "$1" ]
then
  NEWER_BRANCH=$(git rev-parse --abbrev-ref HEAD)
else
  NEWER_BRANCH=$1
fi

if [ "$NEWER_BRANCH" == "$OLDER_BRANCH" ]
then
  echo "  Please supply 2 different branches to compare!"
  show_help
fi

OUT=$(\git cherry $OPT $OLDER_BRANCH $NEWER_BRANCH)

if [ -z "$OUT" ]
then
  echo "No differences found. The branches $NEWER_BRANCH and $OLDER_BRANCH are in sync."
  exit;
fi

if [ "$OPT" == "-v" ]
then
  echo "$OUT"
else
  echo "$OUT" | awk '{print $2}'
fi
Брэд Паркс
источник
1
Это произвело около 100 неактуальных сообщений журнала для меня.
Арли Стивенс
Эй, @ArlieStephens - Я попробовал это, и это все еще работает для меня? Я только добавил справку и больше сообщений об ошибках выше. Дайте мне знать, как это происходит!
Брэд Паркс
Интересный. Глядя на код, который у вас есть сейчас, я думаю, что он может быть таким же простым, как и оригинал, если предположить, что ветвь, о которой я забочусь, вышла из master. С текущим кодом я мог бы явно указать родительскую ветвь. (IIRC, моя ветка разработчика пришла из ветки релиза, а не мастера.)
Арли Стивенс
@ArlieStephens - да, я не думаю, что изменил какую-либо функциональность .... Я обновил ее еще раз и сделал некоторые документы более точными после некоторого локального тестирования ... и добавил опцию "verbose", которая была там раньше, но я не документировал
Брэд Паркс
3

Я использую следующие команды:

git shortlog --no-merges --graph --abbrev-commit master..<mybranch>

или

git log --no-merges --graph --oneline --decorate master..<mybranch>
Alex
источник
1

Я нашел этот подход относительно простым.

Оформить заказ в филиал и чем

  1. Бегать

    git rev-list --simplify-by-decoration -2 HEAD
    

Это обеспечит всего два SHA:

1) последний коммит ветки [C1]

2) и зафиксировать родительский элемент для первого коммита ветви [C2]

  1. Теперь беги

    git log --decorate --pretty=oneline --reverse --name-status <C2>..<C1>
    

Здесь C1 и C2 - две строки, которые вы получите при запуске первой команды. Поместите эти значения без <> во второй команде.

Это даст список истории изменения файла в ветке.

Mustkeem K
источник
Если вы прочтете первые три строки вопроса, вы увидите, что автор перечисляет эту точную команду и объясняет, почему это не то, что они ищут
black_fm
Ах, @black_fm Я внес некоторые изменения в ответ. Вы можете проголосовать за него, если сочтете это полезным сейчас. :)
Mustkeem K
0

В моей ситуации мы используем Git Flow и GitHub. Все, что вам нужно для этого, - это сравнить вашу ветку функций с вашей веткой разработки на GitHub.

Он покажет коммиты, сделанные только для вашей функциональной ветви.

Например:

https://github.com/your_repo/compare/develop...feature_branch_name

peterpengnz
источник
Я согласен, что когда вы попадаете в ситуацию «я хочу посмотреть, как будет выглядеть запрос на извлечение», лучшим решением будет часто игнорировать gitи вместо этого использовать GitHub, чтобы просто выполнить запрос на извлечение или сравнить ветви.
pkamb