🔄
Хостинг

git pull билан сайтни янгилаш: хостингда оддий ва ишончли деплой

14.11.2026
← Барча мақолалар

Кичик ва ўрта сайтларда деплой жараёни кўпинча FTP орқали файлларни қўлда кўчиришдан иборат бўлади. Бу усул ишлайди, лекин қайси файл ўзгаргани эсдан чиқади, эски файллар серверда қолиб кетади ва икки дастурчи бир вақтда ишласа, бир-бирининг ўзгаришини устидан ёзиб юборади. Агар лойиҳа Git репозиториясида сақланса, серверни ҳам ўша репозиториянинг бир нусхаси сифатида кўриш мумкин. Шунда янгилаш битта git pull буйруғига айланади ва сервердаги код ҳар доим аниқ бир commit'га мос келади.

Бу усул қачон тўғри келади

git pull билан деплой қилиш оддий PHP сайтлар, WordPress мавзуси ёки плагини, Laravel лойиҳаси ва статик саҳифалар учун яхши ишлайди. Шарт битта: хостингингизда SSH кириш ва серверда git дастури бўлиши керак. sayt.uz хостингида SSH тарифингизда бор-йўқлигини кабинетдан ёки техник ёрдам орқали билиб оласиз, VPS'да эса Git'ни ўзингиз ўрнатасиз. Агар лойиҳа Node.js ёки фронтенд йиғишни (build) талаб қилса, pull'дан кейин қўшимча буйруқлар керак бўлади, буни қуйида кўриб чиқамиз.

Шуни ҳам айтиш керакки, бу усул ҳар бир янгиланишда бир неча сониялик "оралиқ ҳолат" яратади: файлларнинг бир қисми янгиланган, бир қисми ҳали эски бўлади. Оддий сайтларда бу сезилмайди, лекин катта трафикли дўконларда узилишсиз деплой учун release папкалари ва symlink усулидан фойдаланган маъқул.

Серверда деплой калит яратиш

Репозитория ёпиқ (private) бўлса, сервер унга кириш учун ўз калитига эга бўлиши керак. Шахсий GitHub ёки GitLab паролингизни серверга ёзиш хато, бунинг ўрнига фақат ўқиш ҳуқуқига эга "deploy key" ишлатилади. Сервер терминалида калит яратилади ва унинг очиқ қисми репозитория созламаларидаги Deploy keys бўлимига қўшилади.

ssh-keygen -t ed25519 -C "deploy@example.uz" -f ~/.ssh/deploy_key -N ""
cat ~/.ssh/deploy_key.pub   # shu qatorni repozitoriyaning Deploy keys bo'limiga qo'shing

# ~/.ssh/config ga yozuv:
Host github.com
    IdentityFile ~/.ssh/deploy_key
    IdentitiesOnly yes

Уланишни ssh -T git@github.com буйруғи билан текширасиз: муваффақиятли бўлса, GitHub сизни танигани ҳақида хабар қайтаради. Деплой калитни "фақат ўқиш" режимида қолдириш муҳим, шунда сервер бузилган тақдирда ҳам ҳужумчи репозиториянгизга код ёза олмайди.

Биринчи клонлаш ва папка тузилиши

Кўп хостингларда сайтнинг илдиз папкаси олдиндан белгиланган бўлади, масалан ~/www/example.uz. Агар папка бўш бўлса, репозиторияни тўғридан-тўғри унга клонлаш мумкин. Папкада аллақачон файллар бўлса, аввал уларнинг захира нусхасини олиб, кейин папкани бўшатиб клонлаш хавфсизроқ.

cd ~/www
git clone git@github.com:misol/example-sayt.git example.uz
cd example.uz
git log --oneline -1   # serverda qaysi commit turganini ko'rish

Энг кўп эътибордан четда қоладиган нарса бу .git папкаси. Сайт илдизида турган бу папка бутун лойиҳа тарихини, баъзан эски паролларни ҳам сақлайди ва веб-сервер уни очиқ берса, исталган одам кодни юклаб олиши мумкин. Apache'да қуйидаги қоида .htaccess файлига қўшилса, бундай сўровлар рад этилади; Nginx'да эса шунга ўхшаш location қоидаси керак. Янада яхши ечим, агар фреймворк имкон берса, веб-илдизни public каби ички папкага йўналтиришдир.

# .htaccess (Apache)
RedirectMatch 404 /\.git

# Nginx
location ~ /\.git { deny all; return 404; }

.gitignore ва сервердаги махфий файллар

Серверга хос файллар, яъни .env, wp-config.php, фойдаланувчилар юклаган расмлар ва кеш папкалари репозиторияга кирмаслиги керак. Акс ҳолда ҳар бир git pull уларни локал нусха билан алмаштириб юборади ёки, аксинча, ишлаб турган сервердаги пароллар GitHub'га чиқиб кетади. Шунинг учун .gitignore файлида бу йўлларни аниқ кўрсатиб қўйинг.

.env
wp-config.php
storage/logs/
wp-content/uploads/
vendor/
node_modules/

Янгилаш скрипти

Ҳар сафар бир нечта буйруқни қўлда териш ўрнига уларни кичик скриптга йиғиб қўйиш қулай. Қуйидаги мисол Laravel лойиҳаси учун: у аввал локал ўзгаришлар йўқлигини текширади, кейин фақат "fast-forward" усулида янгилайди, яъни сервердаги тарих репозиториядагидан ажралиб қолган бўлса, бирлаштиришга уринмасдан тўхтайди.

#!/bin/bash
set -e
cd ~/www/example.uz

if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
  echo "Serverda commit qilinmagan o'zgarishlar bor, to'xtatildi"; exit 1
fi

git fetch origin
git pull --ff-only origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
echo "Deploy tugadi: $(git log --oneline -1)"

Скриптни deploy.sh номи билан сақлаб, chmod +x deploy.sh буйруғи билан бажариладиган қиласиз. WordPress мавзуси учун Composer ва миграция қаторлари керак эмас, одатда фақат git pull ва кеш плагинини тозалаш етарли.

Мисол: Самарқанддаги ўқув маркази сайти

Самарқанддаги ўқув марказининг сайти оддий PHP'да ёзилган ва унда курслар жадвали ҳамда рўйхатдан ўтиш формаси бор. Аввал дастурчи ҳар бир тузатишдан кейин ўнлаб файлларни FTP орқали юкларди ва бир марта эски версиядаги форма файлини юклаб, бир кун давомида аризалар келмай қолган. Git'га ўтгач у ўзгаришларни ноутбукда қилиб, git push билан GitHub'га юборади, сўнг серверда ./deploy.sh ни ишга туширади. Хато чиқса, git log орқали олдинги commit'ни топиб, git checkout билан бир дақиқада қайтаради.

Тез-тез учрайдиган хатолар

"Your local changes would be overwritten by merge" хабари серверда кимдир файлларни тўғридан-тўғри таҳрирлаганини билдиради. Бу ҳолатда аввал git diff билан ўзгаришни кўриб чиқинг: керакли бўлса, уни репозиторияга кўчиринг, кераксиз бўлса git checkout -- fayl билан бекор қилинг. "Permission denied (publickey)" эса деплой калит қўшилмаган ёки config файли нотўғри эканини кўрсатади. Файллар эгаси нотўғри бўлса, яъни клонлаш root номидан қилинган бўлса, сайт юклаган расмларни сақлай олмай қолади, шунинг учун Git буйруқларини доим сайт эгаси бўлган фойдаланувчи номидан бажаринг.

Ўхшаш мақолалар

💾 VPS снапшот ва захира нусха (бэкап) фарқи: қайси бири сизни қутқаради 🔀 Ҳар бир сайтга алоҳида PHP версияси: ISPmanager'да созлаш 🖥️ Ўзбекистонда хостинг танлаш 2026: маҳаллий ёки хорижий сервер 🗂️ Inode тугади: дискда жой бор, лекин файл яратиб бўлмаяпти — сабаб ва ечим
🌐 Тил
🇺🇿 O'zbek 🇺🇿 Ўзбек ✓ 🇷🇺 Русский 🇬🇧 English