Проверьте, есть ли что-нибудь новое в локальной истории git по сравнению с удаленным - PullRequest
0 голосов
/ 14 октября 2019

Я хочу (программно) убедиться, что моя локальная копия истории удаленных git точна, то есть, что локальная история git содержит точно такую ​​же историю, что и история удаленных git (не больше, не меньше).

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

(Я программирую на Java с использованием JGit, но я думаю, что ответ, использующий командную строку git, тоже подойдет, так как его легко перевести на Java-программу.)

Я знаю, как программно извлекать, и я проверил, работает ли он в простом случае с одной ветвью master, которая отслеживает ветку origin / master. Но я не уверен, что простой git fetch обязательно выберет все, что находится на пульте, которого у меня нет локально, так как полагаю, что это зависит от статуса отслеживания.

И наоборот, я игнорирую, как проверитьпросто, что не существует более свежей фиксации локально, которая еще не была передана на удаленный.

Я хочу гарантировать, что локальная история такая же, как и удаленная, даже в случае, если кто-то вручную поиграл слокальная копия git (например, настроила нечетное состояние отслеживания), насколько это возможно. Я понимаю, что невозможно что-либо гарантировать, если столкнуться с состязательным поведением, если запретить повторную загрузку всего и сравнение всего (например, кто-то может злонамеренно изменить указатели origin /… в локальной копии). Я работаю в предположении, что мой пользователь не пытается злонамеренно вызвать сбой или неправильное поведение моей программы. Я просто хочу иметь возможность предупредить моего пользователя, если есть основания полагать, что локальная копия, на которую действует моя программа, похоже, была изменена по сравнению с удаленной, и предложить вариант для повторной загрузки (но не беспокоить его, если этокажется ненужным).

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

Меня не волнует, что что-то осталось в области подготовки или в рабочем дереве, меня интересует только история git (в папке .git). И мне все равно, писать что-нибудь локально или удаленно. Это только о чтении данных.

1 Ответ

1 голос
/ 14 октября 2019

Необходимо убедиться, что к локальному хранилищу применяются несколько ограничений:

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

    (Это ограничение может быть немного ослаблено, если вы также захотите принудительно форсировать все other хранилища изменяются так, что при необходимости все они совпадают.)

  2. Ваш локальный хранилище должен быть полным клоном удаленного. Это не должен быть ни мелкий клон, ни клон с одной ветвью. (Это условие, которое вы, вероятно, должны просто статически устанавливать во время создания локального клона. Затем оно будет сохраняться, если кто-то намеренно не нарушит его. Тем не менее, вы можете проверить его на мелкость, проверив, существует ли файл $GIT_DIR/shallow. Для этого должен быть git rev-parse тест, но во многих версиях Git его нет. Посмотрите, есть ли у вашей версии Git git rev-parse --is-shallow-repository. Вы можете протестировать одно-ветвление, протестировав результат git config --get-all remote.origin.fetch: сравните результат для обычного репозитория с результатом для клона с одной ветвью, чтобы увидеть его.)

  3. Вы должны выбрать какой-то метод, с помощью которого можно определить имя локальной ветви, например master с соответствующим именем филиала на выбранном пульте. Как правило, поскольку пользователям нравится сопоставлять имена своих ветвей, это тривиально: если удаленный узел имеет имя origin, каждая (локальная) ветвь B соответствует origin/<em>B</em>.

    ЕслиВы выбираете какой-то другой метод, соответственно измените второй шаг ниже.

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

  1. Выполнить:

    git fetch origin
    

    , чтобы все имена удаленного отслеживания былив актуальном состоянии.

  2. Выполнить что-то эквивалентное этому (записано в виде нескольких разделов с некоторыми комментариями). Примечание: это полностью не проверено.

    TF=$(mktemp) || exit
    trap "rm -f $TF 0 1 2 3 15"  # clean up temp file on exit
    valid=true
    

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

    # compare all local branches to their updated remote-tracking counterparts
    git for-each-ref --format='%(refname:short) %(objectname)' refs/heads > $TF
    while read branchname hash; do
        theirs=$(git rev-parse -q --verify refs/remotes/origin/$branchname) || {
            # git rev-parse failed: they don't have this branch name at all
            valid=false
            break
        }
        test $hash = $theirs || { valid=false; break; }
    done < $TF
    if $valid; then
        echo "upstream repository has all the local branches and they match"
    else
        echo "upstream repository does not have some branch or does not match"
        exit 1 # failure
    fi
    

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

    # now make sure a local branch exists for each of its remote-tracking counterparts
    # Note: some or all of this is redundant, but it's easier to re-test here
    git for-each-ref --format='%(refname:short) %(objectname)' refs/remotes/origin > $TF
    while read rtname hash; do
        branchname=${rtname#refs/remotes/origin/}
        ours=$(git rev-parse -q --verify refs/heads/$branchname) || {
            # git rev-parse failed: we don't have this branch name at all
            valid=false
            break
        }
        # no need to test hash: we did that already
    done < $TF
    if $valid; then
        echo "and, we have branches for all their branches"
        echo "so we must be good"
        exit 0
    else
        echo "we're missing some local branch names to match theirs"
        exit 1
    fi
    

    Как и раньше, вы можете выполнить полную перекрестную проверку.

Вероятно, разумно разрешить некоторые ситуации: например, не требуется , что какая-то локальная ветвь B существует только потому, что существует origin/<em>B</em>. Поэтому вы можете полностью пропустить последнюю проверку.

Проверка того, что локальная ветвь B соответствует origin / B, смехотворно проста: идентификаторы хеша либо совпадают, либо нет. Если они совпадают, история такая же. Если нет, то это не так. Причина этого в том, что история в репозитории Git - это набор коммитов. Ветвь name просто содержит необработанный хэш-идентификатор последнего коммита, который Git должен считать «частью ветви». Вся ранняя история определяется коммитами . Коммиты полностью доступны только для чтения, включая их родительские указатели;и каждый коммит имеет уникальный хеш-идентификатор, поэтому, если хеш-идентификаторы совпадают, то и история.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...