git статус по-прежнему говорит: «Ваш филиал впереди по N совершает ". после git ребаз - PullRequest
1 голос
/ 28 февраля 2020

Предположим, у меня есть ветвь my-topic-branch, разветвленная от моей локальной ветки master, и эта ветвь master связана с удаленной веткой master.

Ветвь my-topic-branch была первоначально созданный из тега с именем tag1. tag1 - это тег, наложенный на удаленную главную ветвь, и я вижу этот тег как результат git fetch.

Некоторое время проходит, чтобы другие могли перенести свои изменения в эту удаленную главную ветвь. И еще позже новый tag2 устанавливается кем-то другим (см. Обновление 6 ниже по причинам для этого).

Затем я снова использую git fetch, чтобы убедиться, что У меня есть все эти удаленные теги в моем локальном репо для дальнейших операций.

Я перехожу на этот tag2, например:

$ git rebase tag2
First, rewinding head to replay your work on top of it...
Applying: CENSORED_LOG_MESSAGE1
Applying: CENSORED_LOG_MESSAGE2
Auto packing the repository in background for optimum performance.
See "git help gc" for manual housekeeping.

Но затем я запускаю git status и вижу это :

$ git status
On branch my-topic-branch
Your branch is ahead of 'tag1' by 195 commits.
  (use "git push" to publish your local commits)

Я ожидаю, что вышеупомянутое сообщение должно что-то сказать о tag2, и уж точно не о том, что я опередил старый тег на 195 коммитов.

Почему бы git status сообщить о коммите, с которого разветвился my-topic-branch, а не о новом коммите, на который я в основном недавно перебазировал?

Если это ожидаемое поведение, тогда хорошо, мне просто придется его игнорировать, но это Странно видеть, что я все еще отстаю master от 195 коммитов, когда это определенно не соответствует действительности (если git rebase действительно сделал то, что, как я думаю, должно).

Обновление 1

Я все еще могу найти базовую точку my-topic-branch, если HEAD все еще находится в этой ветви, через:

* 1 044 *

Но все же возникает вопрос о выводе git status.

Обновление 2:

Обновление в ответ на комментарий Pesho_T :

Я побежал git branch -vv и получил следующее. Это подвергается цензуре, но с добавлением чисел, таких как «2» и «4», чтобы отличать их от других, указанных выше:

$ git branch -vv | grep my-topic-branch
* my-topic-branch                                CENSORED_SHA1_TAG1 [tag1: ahead 195] MDFCOR-420 CENSORED_LOG_MESSAGE_TAG1

Обновление 3:

Воспроизведение некоторых команд в Ответ Торека , я вижу:

$ git rev-parse --abbrev-ref @{u}
tag1
$ git rev-parse --symbolic-full-name @{u}
refs/tags/tag1

В настоящее время, из очень хорошей рецензии Торека, я заключаю, что tag1 действительно является тегом, а не ветвью.

Обновление 4:

Я отредактировал предыдущие CENSORED_SHA1, чтобы они были согласованными:

  1. CENSORED_SHA1_TAG1 - это коммит SHA1, соответствующий тегу 1
  2. CENSORED_SHA1_TAG2, это коммит SHA1, соответствующий тегу 2
1077 * Обновление 5:

Еще одно обновление в ответ на ответ Торека :

The upstream of a branch is always another branch name or remote-tracking name, as tag names are forbidden

Я не уверен в этом из-за этого эксперимента:

$ git rev-list --count --left-right my-topic-branch...my-topic-branch@{upstream}
195 0
$ git show -s $(git rev-parse my-topic-branch@{u})
commit CENSORED_SHA1_TAG1 (tag: tag1)
Author: CENSORED_AUTHOR
Date:   CENSORED_DATE

    CENSORED_LOG_MESSAGE_TAG1

Выше показано, что восходящий поток указывает на тег.

Чтобы подтвердить, что оба эти тега tag1 и tag2 находятся в главной ветви, я сделал это, согласно подсказкам, найденным в ответ на Git: Как узнать, на какой ветке находится тег? : * 10 92 *

$ git branch -a --contains $(git rev-parse tag1^{commit}) | grep -E 'my-topic-branch|master'
* my-topic-branch
  master
$

Обновление 6:

В мои планы не входит git push возврат к метке (tag2), по которой я запускал git rebase. Я использую этот тег только как точку на ветви master, чтобы перебазировать my-topic-branch в. tag2 - это известная точка в ветви master, на которой приложение должным образом строится (я не могу go более подробно рассказать об этом по конфиденциальным причинам). Я ожидаю продолжения git rebase для последующих тегов, tag3, tag4 и т. Д. До тех пор, пока my-topic-branch не будет готов к производству, после чего я объединю его с моей локальной веткой master и выполню git push оттуда.

Обновление 7

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

Ответы [ 2 ]

2 голосов
/ 29 февраля 2020

Вероятно, 1 , что вы случайно создали имя ветви и имя тега , которые оба печатаются одинаково при сокращении. [ Редактировать : за обновление 3 это не проблема. Вместо этого, каким-то образом, у вас есть имя тега, установленное как ветка вверх по течению. Я не уверен, как вы попали в это состояние - смотрите результат моей собственной попытки сделать это ниже.] Если у вас есть два таких имени, они могут иметь разные идентификаторы ha sh. Идентификатор ha sh, который вы получаете при разборе , такое имя немного сложно (есть правила, которые описаны в документации gitrevisions , но есть исключения из правил также). Лучше всего выйти из этой ситуации, обычно переименовывая неподходящее имя ветви.

Помните, что имя ветви , подобное master, действительно является именем refs/heads/master; имя тега как v2.25.0 действительно refs/tags/v2.25.0. Поэтому возможно создать ветвь с именем v2.25.0, даже если тег существует, поскольку полное имя ветки refs/heads/v2.25.0, а не refs/tags/v2.25.0. Эти два имени различны, но если вы просматриваете короткие версии каждого из них, оба будут v2.25.0.

Сообщение о подсчете ahead или behind из git status является результатом выполнения:

git rev-list --count --left-right <name1>...<name2>

Обратите внимание, что между двумя именами есть три точки. 2 Два имени:

  • name1 - это ваша текущая ветвь;
  • name2 - это восходящая вашей текущей ветки.

Команда git rev-list в этой форме (с использованием трех точек) находит коммиты, которые достижимы из имени левой стороны, но не имени правой стороны, и коммиты можно получить из имя с правой стороны, но не имя с левой стороны, а затем считает (--count) их, но отдельно (--left-right), а не вместе.

Это означает, что эти значения зависят от вашей текущей ветви (конечно, именно поэтому в нем говорится «ваша ветвь ... впереди») и upstream . Вы управляете настройкой восходящего потока с помощью git branch --set-upstream-to, и вы можете читать восходящий поток вашей текущей ветви с помощью:

$ git rev-parse --abbrev-ref @{u}
origin/master
$ git rev-parse --symbolic-full-name @{u}
refs/remotes/origin/master

Чтобы помочь в случае, когда вы случайно сделали оба имя ветви и имя тега , которые выглядят одинаково при сокращении, используйте вариант --symbolic-full-name.

восходящий ветви - [ Изменить: или должно быть ] всегда другое имя ветви или имя удаленного отслеживания, поскольку имена тегов запрещены:

git branch --set-upstream-to=v2.25.0
fatal: Cannot setup tracking information; starting point 'v2.25.0' is not a branch.

Имена удаленного отслеживания, такие как origin/master, более типичны, но вы можете установить восходящий поток любой ветви на любое другое имя ветви.

Если значение ahead отлично от нуля, это обычно то, что вы видите, и то, что вы видите здесь. Однако, если оба значения отличны от нуля, git status будет использовать слово diverged. Если счет ahead равен нулю, а счет behind не равен нулю, git status печатает счет behind. Если оба значения равны нулю - так что ветвь синхронизируется c с восходящим потоком - git status говорит Your branch is up to date with ....

Подробнее о синтаксисе из трех точек см. документацию gitrevisions . Чтобы понять достижимость , см. Думайте как (а) Git. Для краткой иллюстрации c рассмотрим следующий рисунок:

          I--J   <-- branch1
         /
...--G--H   <-- master
         \
          K--L   <-- branch2, origin/branch1

Имя branch1 "впереди 2" из master, потому что из коммита J мы возвращаемся к I и затем H, что означает, что коммиты I-J доступны с branch1, но не с master. Аналогично, branch2 на 2 опережает master, но два из них являются коммитами K-L. Это означает, что master на 2 отстает от branch1 или branch2, с этими двумя коммитами, равными I-J или K-L соответственно. В то же время, branch1 имеет отклонение от origin/branch1, потому что он на 2 впереди (I-J) и 2 позади (K-L).


1 Вы можете попасть в подобные хитрые ситуации, если вы переместите тег, потому что теги должны быть универсальными во всех Git репозиториях, но теги также никогда не предназначены для ход . Как только один репозиторий Git имеет тег, он будет склонен предполагать, что копия, которую имеет , имеет правильный , даже если кто-то принудительно переместил тег в какой-то другой Git хранилище, что этот Git хранилище должно соответствовать. Но это показало бы другие симптомы, потому что вы не можете установить в восходящем направлении ветви имя тега.

2 Синтаксис из трех точек создает симметрию c разница , в терминах теории множеств. Поскольку это симметрия c, вы можете поменять местами два имени, если помните, что два подсчитывают , которые напечатает git rev-list --count --left-right, теперь также поменялись местами.

0 голосов
/ 09 марта 2020

Резюме

Теперь я считаю, что комментарий Торека является ключевым аспектом. Ниже я продемонстрирую, что вместо использования git push -u на локальном мастере для установки восходящего потока в этой ветви ранее в процессе.

MCVE

я обнаружил, что причина root почему git показывало состояние:

Your branch is ahead of 'tag1' by N commits

в том, что локальная главная ветвь не имела восходящего потока, потому что -u не был передан начальному git push. Это показано ниже с MCVE. Он все еще минимален, потому что я не мог избежать использования рабочих деревьев здесь (у меня есть рабочие деревья в моем реальном сценарии). Я не включил подробности об использовании рабочих деревьев в мою OP, потому что это не считалось уместным. Я до сих пор не думаю, что это так, но я не могу воспроизвести проблему без использования рабочих деревьев. Таким образом, возможно, рабочие деревья являются важным фактором. Возможно, кто-то может объяснить нам, почему это так, или дополнительно минимизировать мой демонстрационный скрипт соответственно.

Демонстрационный скрипт

Сценарий Bash (Linux), приведенный ниже, выполняет функция дважды, чтобы воспроизвести мой сценарий двумя способами: один с git push -u и снова с git push (без -u):

#/!/bin/bash

change () {
  local file="$1"
  local change_text="$2"
  echo "$change_text" > $file
  git add $file
  git commit -m "$change_text"
}

log () {
  (
    set +x
    echo
    echo "Current branch:   $(git symbolic-ref --short HEAD)"
    echo "Current upstream: $(git rev-parse --symbolic-full-name @{u})"
    git log --oneline --graph --decorate --parents
    echo
  )
}

replay () {
  local scratch_dir="$1"
  local master_push_args="$2"
  test -z "$1" && { echo "USAGE: replay scratch_dir"; exit 1; }
  (
    echo
    echo "Generating Git scenario into ${scratch_dir} ..."

    set -e -x

    # Create a scratch area:
    rm -rf $scratch_dir
    mkdir $scratch_dir
    cd $scratch_dir

    # Create the bare repo for some scrubber product:
    mkdir scrubber
    cd scrubber
    git init --bare
    main_git_repo=$(pwd)
    cd ..

    git clone $main_git_repo bootstrap
    cd bootstrap

    # Add some changes to it:
    change file1 "change 1.1"
    change file1 "change 1.2"
    git tag tag1 -m "tag1"
    git push $master_push_args
    git push --tags
    log
    cd ..

    # Create a mirror:
    git clone --mirror $main_git_repo my_mirror
    mirror_dir=$(readlink -f my_mirror)

    # Create a worktree
    GIT_DIR=$mirror_dir git worktree add scrubber1
    cd scrubber1

    # Add some changes to a new branch:
    #
    #   Avoid conflicts during subsequent rebase by changing a different file.
    #
    git checkout -b my-topic-branch tag1
    change file2 "change 2.1"
    change file2 "change 2.2"
    change file2 "change 2.3"
    log

    # Add some changes to master:
    git checkout master
    change file1 "change 3.1"
    change file1 "change 3.2"
    change file1 "change 3.3"
    change file1 "change 3.4"
    git tag tag2 -m "tag2"
    change file1 "change 4.1"
    change file1 "change 4.2"
    git push $master_push_args
    log

    git checkout my-topic-branch
    git rebase tag2
    git status
    log
  ) 2>&1 # for subsequent filtration (e.g., ... | grep/sed/awk/etc.)
}

filter () {
  grep -A2 'git status'
}

# Show the version I'm using:
echo "Git Version: $(git --version)"

echo
echo "Using git push on master without -u ..."
replay /tmp/scenario1 | filter

echo
echo "Using git push on master with -u ..."
replay /tmp/scenario2 -u | filter

Выполнение этого сценария дает два результата из состояния git:

Git Version: git version 2.20.1

Using git push on master without -u ...
+ git status
On branch my-topic-branch
Your branch is ahead of 'tag1' by 7 commits.

Using git push on master with -u ...
+ git status
On branch my-topic-branch
Your branch is up to date with 'my-topic-branch'.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...