Заблокируйте нажатие ветки git

Вот такая ситуация:

У меня есть общедоступный репозиторий для моего приложения с открытым исходным кодом на github.com. Однако теперь я хотел бы написать конкретный код, который не будет общедоступным (я мог бы использовать его в коммерческой версии своего приложения).

Я решил, что могу использовать тот же репозиторий, и я создам «частную» ветку в моем репозитории git, которую я не буду публиковать.

Но ошибки случаются. Есть ли способ запретить git когда-либо отправлять ветку на удаленные серверы?

Если есть лучший способ справиться с этой ситуацией, я, конечно, приветствую любые предложения.


person houbysoft    schedule 10.07.2011    source источник


Ответы (7)


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

Вот пример.

$ git clone [email protected]:user/repo.git
$ cd repo
$ git checkout -b secret
$ echo "This is just a dummy to prevent fast-forward merges" > dummy.txt
$ git add .
$ git commit -m "Dummy"
$ git push origin secret

Теперь, когда фиктивная ветка настроена, мы можем воссоздать ее локально, чтобы отличаться от ветки на GitHub.

$ git checkout master
$ git branch -D secret
$ git checkout -b secret
$ echo "This diverges from the GitHub branch" > new-stuff.txt
$ git add .
$ git commit -m "New stuff"

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

$ git push origin secret
To [email protected]:user/repo.git
! [rejected]        secret -> secret (non-fast forward)
error: failed to push some refs to ‘[email protected]:user/repo.git’
person hammar    schedule 11.07.2011
comment
Извините, не могли бы вы подробнее объяснить, что вы имеете в виду, говоря, что это не будет слияние с быстрой перемоткой вперед? Спасибо - person houbysoft; 11.07.2011
comment
Отправьте фиксацию в фиктивную ветку, которая не будет в вашей настоящей ветке, чтобы они разошлись. - person hammar; 11.07.2011
comment
Отлично! Это сработает, если у вас есть как публичное (происхождение), так и частное репо (частное). Просто поместите фиктивную ветку только в публичное репо. Тогда git push private secret будет работать, но git push origin secret - нет. - person Benjamin Atkin; 02.08.2011
comment
Менее хакерский метод - написать хук pre-push и включить его в ваше локальное репо, но этот скрипт нужно будет разместить в каждом репо с доступом к ветке и возможностью ее отправки. - person Dan Hunsaker; 20.01.2015
comment
ИМХО, лучше предпушить. Это решение не сработает для тех, кому нравится "git push -f". У меня есть небольшой публичный репозиторий, в котором я делюсь моими точечными файлами. Поэтому нет ничего страшного в использовании 'git push -f' (я перебазирую / сбрасываю и т. Д., Чтобы история была чистой и аккуратной :(). Но мне действительно нужно убедиться, что частная ветка, содержащая мою конфиденциальную информацию, никогда не будет отправлена ​​в удаленный. - person Lungang Fang; 11.11.2015
comment
Еще проще для GitHub создать ветку на GitHub и включить для нее защиту ветки и потребовать проверки запросов на вытягивание или проверки статуса (и обязательно включите администраторов). Тогда GitHub не позволит вам пушить ветку даже с -f. - person asmeurer; 28.07.2018

Вот как работает подход ловушки pre-push с ветвью dontpushthis.

Создайте этот файл как .git/hooks/pre-push:

if [[ `grep 'dontpushthis'` ]]; then 
  echo "You really don't want to push this branch. Aborting."
  exit 1
fi

Это работает, потому что список отправляемых ссылок передается на стандартный ввод. Таким образом, это также поймает git push --all.

Сделайте его исполняемым.

Сделайте это в каждом локальном репозитории.

Когда вы попытаетесь нажать на эту ветку, вы увидите:

$ git checkout dontpushthis
$ git push
You really don't want to push this branch. Aborting.
error: failed to push some refs to 'https://github.com/stevage/test.git'

Очевидно, это так просто, как кажется, и предотвращает только нажатие ветки с именем «dontpushthis». Так что это полезно, если вы пытаетесь избежать прямого перехода к важной ветке, такой как master.

Если вы пытаетесь решить проблему предотвращения утечки конфиденциальной информации, этого может быть недостаточно. Например, если вы создали дочернюю ветвь из dontpushthis, эта ветка не будет обнаружена. Вам понадобится более сложное обнаружение - например, вы можете посмотреть, присутствовали ли какие-либо коммиты в ветке «dontpushthis» в текущей ветке.

Более безопасное решение

Еще раз взглянув на вопрос, я думаю, что лучшим решением в этом случае будет:

  1. Иметь одно публичное репо
  2. Клонируйте это в новый рабочий каталог, который является частным
  3. Удалите пульт (git remote rm origin) из этого рабочего каталога.
  4. Чтобы объединить общедоступные изменения, просто выполните git pull https://github.com/myproj/mypublicrepo

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

person Steve Bennett    schedule 27.05.2015
comment
это то, что я использую `` #! / bin / sh if [[grep -e develop -e master]]; then echo Пожалуйста, не нажимайте напрямую, чтобы развернуть или освоить exit 1 fi '' - person Jacob Evans; 24.06.2016
comment
Более безопасное решение намного лучше. Спасибо! - person Julio Saito; 30.09.2016
comment
Мне также больше нравится более безопасное решение! Вместо 3. Я бы сделал git remote set-url --push origin http://no-push-to-this-remote:99999/ Это устанавливает только URL-адрес push этого пульта дистанционного управления на недопустимый URL-адрес (порт 99.999 недействителен), поэтому возможна обычная выборка / извлечение, просто нажатие на этот пульт завершится с ошибкой с немедленным сообщением об ошибке. . Это немного удобнее, чем всегда указывать URL-адрес для выборки. - person Stefan; 25.06.2019

Доработка скрипта .git / hooks / pre-push от @ steve-bennett

    #!/usr/bin/bash

    branch_blocked=mine

    if grep -q "$branch_blocked"; then
        echo "Branch '$branch_blocked' is blocked by yourself." >&2
        exit 1
    fi
person Lungang Fang    schedule 11.11.2015

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

Идея состоит в том, чтобы начать сообщение фиксации первой фиксации вашей частной ветки со слова PRIVATE:.

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

Вот шаги:

  • Создать файл в .git/hooks/pre-push
  • Дайте ему права на исполнение
  • Прошедший в нем следующий скрипт

    #!/bin/sh
    
    remote="$1"
    url="$2"
    
    z40=0000000000000000000000000000000000000000
    
    IFS=' '
    while read local_ref local_sha remote_ref remote_sha
    do
            if [ "$local_sha" = $z40 ]
            then
                    # Handle delete
                    :
            else
                    if [ "$remote_sha" = $z40 ]
                    then
                            # New branch, examine all commits
                            range="$local_sha"
                    else
                            # Update to existing branch, examine new commits
                            range="$remote_sha..$local_sha"
                    fi
    
                    # Check for WIP commit
                    commit=`git rev-list -n 1 --grep '^PRIVATE:' "$range"`
                    if [ -n "$commit" ]
                    then
                            echo "Error: Found PRIVATE commit in $local_ref."
                            echo "The commit is in the range $range."
                            echo "NOT pushing!"
                            exit 1
                    fi
            fi
    done
    
    exit 0
    
    remote="$1"
    url="$2"
    

Пример неудачи

$ git push origin private/old-kalman-filter 
Found PRIVATE commit in refs/heads/myforbiddenbranch, the commit is in the range 
a15c7948676af80c95b96430e4240d53ff783455. NOT PUSHING!
error: failed to push some refs to 'remote:/path/to/remote/repo'

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

Этот сценарий можно изменить так, чтобы он учитывал только один запрещенный пульт, установив флажок remote_ref. Но в этом случае не забудьте скопировать этот хук во все репозитории, которым разрешено получать эту ветку.

person Aquadarius    schedule 13.05.2016
comment
Это очень просто. Я также добавил проверку имени пульта, так что если удаленное имя начинается со строки private, push продолжится без дальнейших проверок. Таким образом, у меня может быть частный пульт (например, для резервного копирования), на который я могу отправлять все, и общедоступный, с общими материалами и без личных частей. Спасибо! - person polettix; 26.07.2016

Есть несколько решений:

  1. Нетехнический, просто измените лицензию на коммерческую для вашего филиала
  2. Создайте частный репозиторий на github, содержащий вашу вилку
  3. Сделайте git-hook на сервере (afaik невозможно с github)
  4. Напишите оболочку для git-push, чтобы предотвратить нажатие с помощью git push
person Ulrich Dangel    schedule 10.07.2011
comment
Вы также можете использовать хуки на стороне клиента; в данном случае pre-push - ваш кандидат. - person Dan Hunsaker; 20.01.2015

Вы можете создать ветку, которой нет в вашем удаленном репозитории.

Таким образом, если вы просто сделаете:

git push origin

он будет отправлять только те ветки, которые существуют в удаленном репозитории.

Также после создания ветки просмотрите файл .git/config (в каталоге локального репозитория) - вы увидите, что каждой локальной ветке может быть назначен другой удаленный репозиторий. Вы можете воспользоваться этим, назначив эту ветку отдельному (частному) репозиторию, но это не универсальное решение (ветвь по-прежнему может быть отправлена ​​на удаленный источник, если это явно указано, или с помощью команды git push origin).

person Tadeck    schedule 11.07.2011
comment
@manojlds: Технически существует ровно два if;) Серьезно, объясните, что вы имели в виду. - person Tadeck; 11.07.2011
comment
Обычно я и многие из тех, кого я знаю, делаю git push remote branch - это сводит на нет оба ваших "если" - person manojlds; 11.07.2011
comment
@manojlds: если вы прикажете git загрузить вашу частную ветку в частный репозиторий (git push origin my_private_branch), почему вы думаете, что git не должен этого делать? И на самом деле большинство людей, которых я знаю, используют только git push (или git push [remote]), если есть несколько веток, пульты и запись git push origin my_private_branch, кажется, намного менее эффективны для записи 100 раз в день. - person Tadeck; 11.07.2011
comment
why do you think git should not do it? - вот в чем суть этого вопроса. Возможно, вы правы во второй части. - person manojlds; 11.07.2011

Если вы используете GitHub, вы можете создать ветку на GitHub с тем же именем, что и ваша ветка. Нет необходимости подталкивать к нему какие-либо коммиты, просто создайте пустую ветку от мастера или что-то еще (вы можете сделать это в интерфейсе GitHub, набрав ветку во всплывающем меню «ветка» и щелкнув create branch <branch-name>).

Затем перейдите к настройкам ветки для репозитория (например, https://github.com/<user>/<repo>/settings/branches/<branch-name>) и включите защиту ветки для своей ветки. Обязательно установите все флажки, в частности, флажки требовать проверки или состояния, которые запрещают прямую отправку ветки (ее нужно будет отправить из запроса на перенос), а также обязательно установите флажок, чтобы включить администраторы.

Тогда GitHub не позволит вам нажать на ветку, даже если вы используете -f. Вы получите сообщение вроде

$ git push -f origin private
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 981 bytes | 981.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
remote: error: GH006: Protected branch update failed for refs/heads/private.
remote: error: At least 1 approving review is required by reviewers with write access.
To github.com:<user>/<repo>.git
 ! [remote rejected] private -> private (protected branch hook declined)
error: failed to push some refs to '[email protected]:<user>/<repo>.git'
person asmeurer    schedule 27.07.2018