Блокирайте git клон от натискане

Ето каква е ситуацията:

Имам публично хранилище за моето приложение с отворен код на github.com. Сега обаче бих искал да напиша някакъв специфичен код, който няма да бъде публичен (може да го използвам в комерсиална версия на моето приложение).

Реших, че мога да използвам същото хранилище и бих създал „частен“ клон в моето git хранилище, което няма да натискам.

Но грешките се случват. Има ли някакъв начин да забраните на git да изпраща клон към отдалечени сървъри?

Ако има по-добър начин за справяне с тази ситуация, разбира се, ще приветствам всякакви предложения.


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


Отговори (7)


Малко хакерско решение: Направете фиктивен клон в GitHub със същото име като истинския ви клон и се уверете, че това няма да бъде сливане с бързо превъртане. По този начин операцията за натискане ще се провали.

Ето един пример.

$ 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
IMHO, предварителното натискане е по-добро. Това решение няма да работи за тези, които харесват мен, че 'git push -f'. Имам публично малко хранилище, което споделя моите dot файлове. Следователно не е голяма работа да '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 Моля, не натискайте директно, за да разработите или да управлявате изход 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 адреса за насочване на това дистанционно на невалиден 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
Това е много ясно. Добавих и проверка на името на дистанционното, така че ако името на дистанционното започва с частния низ, натискането ще продължи без допълнителни проверки. По този начин мога да имам частно дистанционно (напр. за резервни причини), където мога да натискам всичко, и обществено с общите неща и без частни части. Благодаря! - person polettix; 26.07.2016

Има множество решения:

  1. Нетехнически, просто коригирайте лиценза към търговски за вашия клон
  2. Направете частно хранилище в github, което съдържа вашия fork
  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: Технически има точно две ако ;) Сериозно, моля, обяснете какво имате предвид. - person Tadeck; 11.07.2011
comment
Обикновено аз и много хора, които познавам, правят git push remote branch - това прави невалидни и двете ви if - 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 със същото име като вашия клон. Няма нужда да налагате ангажименти към него, просто направете празен клон от master или каквото и да е (можете да го направите в интерфейса на 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