Персональна освітня сорінка
by Pavlo Shcherbukha
Зацікавило питання як на своєму комп’ютері налаштувати локальний і віддалений репозиторії (типу резервний зробити). Ну, не завжди хочеться засмічувати GitHub якимись тестами своїми. З іншого боку, хочеться використати звичні інструменти для архівування та відгалужування коду.
Налаштування віддаленого репозиторія на локальній машині
1.1. Створення bare repository:
Створення нової папку, наприклад, my_project.git, і перейти у неї в терміналі.
$ mkdir /path/to/my_project.git
$ cd /path/to/my_project.git
$ git init --bare
Bare repository відрізняється від звичайного тим, що він не має робочої директорії з файлами проєкту. Він містить лише внутрішні дані Git, такі як історія коммітів, гілки та теги (тобто, лише вміст папки .git). Такий репозиторій ідеально підходить для використання як “центральне сховище”, з якого розробники будуть завантажувати й надсилати зміни. Зазвичай такі репозиторії називають з суфіксом .git, щоб підкреслити їхню “бездротову” природу.
1.2. Створення локального репозиторію:
Створіть робочу папку для вашого проєкту. Якщо у вас вже є проєкт, просто перейдіть у його директорію.
$ mkdir /path/to/my_project
$ cd /path/to/my_project
$ git init
1.3. Прив’язка локального репозиторію до bare:
Тепер, коли у вас є локальний репозиторій, додайте ваш bare repository як віддалений. Зазвичай йому дають назву origin за замовчуванням.
$ git remote add origin /path/to/my_project.git
1.4. Перший push:
Додайте файли у свій локальний репозиторій, зробіть перший комміт, а потім надішліть (push) його у віддалений (bare) репозиторій.
$ touch README.md
$ git add README.md
$ git commit -m "Initial commit"
$ git push -u origin master
Тепер можна працювати з локальним репозиторієм, робити комміти та періодично синхронізувати його з вашим “бекапом” за допомогою команд git push та git pull. Це дозволить мати повну історію версій і локальний “бекап” без використання сторонніх сервісів.
А якщо bear репозиторій розмістити на іншій машині. Як між ними організувати спілкування?
Для того, щоб це працювало, потрібно налаштувати доступ по SSH між вашим комп’ютером (client) і машиною, де буде розміщено bare repository (server).
2.1. На машині-сервері (server):
Створити користувача Git (наприклад, git), який буде керувати репозиторіями. У домашній директорії цього користувача створити папку з вашим bare repository:
$ mkdir my_project.git
$ cd my_project.git
$ git init --bare
2.2. На машині-клієнті (client):
Створити пару SSH-ключів. Публічний ключ (id_rsa.pub) потрібно скопіювати на сервер в файл ~/.ssh/authorized_keys користувача git. Додати віддалений репозиторій до локального проекту, використовуючи SSH-адресу:
$ git remote add origin ssh://git@<IP_адреса_сервера>/path/to/my_project.git
2.3. Тепер можна надіслати (push) зміни на сервер:
$ git push origin master
Як для мене, то прийнятне рішення для особистих проектів, що потребують локального бекапу та синхронізації між кількома пристроями.
## 3. Деякі команди git, що використовую
### 3.1. Отримати красивий лог змін
git log --pretty=format:"%h - %an, %ar : %s" --since=2.weeks
f4a2b1c - Jane Doe, 3 days ago : Fix critical bug in user authentication
a9b8c7d - John Smith, 5 days ago : Add feature for data export
e6f5g4h - Jane Doe, 1 week ago : Refactor payment processing module
%h: Відображає короткий commit hash.
%an: Відображає ім’я автора.
%ar: Відображає відносний час від поточної дати ( “2 days ago”, “5 hours ago”).
%s: Відображає перший рядок commit message.
–since=2.weeks: Вказує на проміжок часу (останні 2 тажні) за який відрображати лог.
Відносні проміжки часу
Можна використовувати різні одиниці виміру часу.
Секунди, хвилини, години, дні:
--since=30.minutes (за останні 30 хвилин)
--since=12.hours (за останні 12 годин)
--since=10.days (за останні 10 днів)
Місяці та роки:
--since=3.months (за останні 3 місяці)
--since=1.year (за останній рік)
Комбіновані вирази: Ви можете поєднувати різні одиниці.
--since="1.month 2.days ago" (місяць і два дні тому)
Звісно! Git надає багато гнучких способів для фільтрації комітів за часом. Ось основні альтернативи та доповнення до –since=2.weeks.
Відносні проміжки часу
Ви можете використовувати різні одиниці виміру часу.
Секунди, хвилини, години, дні:
--since=30.minutes (за останні 30 хвилин)
--since=12.hours (за останні 12 годин)
--since=10.days (за останні 10 днів)
Місяці та роки:
--since=3.months (за останні 3 місяці)
--since=1.year (за останній рік)
Комбіновані вирази: Ви можете поєднувати різні одиниці.
--since="1.month 2.days ago" (місяць і два дні тому)
Конкретні дати та час
Можна вказувати точні дати у різних форматах.
Формат YYYY-MM-DD:
--since=2025-09-15 (з 15 вересня 2025 року)
"Людський" формат:
--since="Sep 15 2025"
--since="yesterday" (за вчора і сьогодні)
--since="10:00" (від сьогоднішнього ранку о 10:00)
--since="2025-09-20 10:00:00" (точна дата і час)
git log -- ./iib_app_srvc/post_storedocument_ProcessInput.esql
git log -- ./iib_app_srvc/
commit d80c6a82e28693fb46c09664a453d0c8ded3a3ee Author: pasha pasha@mail.com Date: Mon Nov 25 18:14:02 2024 +0200
Future 1
commit 89d350b3214aaf1883dfe9a0f31286111fb9f4a4 Author: pasha pasha@mail.com Date: Mon Nov 25 07:19:42 2024 +0200
Fix 22 added new field check
### <a name="p3.3">3.3. Які зміни в файли внесені у вказаному комміті</a>
```bash
git show b0f8d8f83a1bfc7f2dbfb9560e0423e13e6c243b
git show b0f8d8f83a1bfc7f2dbfb9560e0423e13e6c243b
commit b0f8d8f83a1bfc7f2dbfb9560e0423e13e6c243b
Author: pasha <pasha@mail.com>
Date: Sat Sep 20 20:39:42 2025 +0300
az-29
diff --git a/function_app.py b/function_app.py
index a73266f..f57a20f 100644
--- a/function_app.py
+++ b/function_app.py
@@ -40,7 +40,7 @@ async def hello_orchestration_starter(req: func.HttpRequest, client: df.DurableO
# Start the orchestration and return a status response
try:
- logging.info(f" Starting orchestration with blob name = {blob_name}")
+ #logging.info(f" Starting orchestration with blob name = {blob_name}")
#, client_input={"name": blob_name}
instance_id = await client.start_new(function_name)
response = client.create_check_status_response(req, instance_id)
@@ -77,7 +77,7 @@ def hello_orchestration_orchestrator(context: df.DurableOrchestrationContext):
result1 = yield context.call_activity("hello_orchestration_activity", "Seattle")
result2 = yield context.call_activity("hello_orchestration_activity", "Tokyo")
result3 = yield context.call_activity("hello_orchestration_activity", "London")
- result4 = yield context.call_activity("video_orchestration_activity", 'blob_name')
+ result4 = yield context.call_activity("video_orchestration_activity", "blob_name")
logging.info(f"Orchestration completed with results: {[result1, result2, result3, result4]}")
Перед виконанням треба закоммітити останні зміни, або зробити git stash. Пілся виконання закоммічені зміни відправити в віддалений репозиторій використовуючи git push.
git fetch origin
git rabase
git fetch + git rebase
Це двокроковий процес, який дає більше контролю і створює чистішу історію.
git fetch: завантажує зміни з віддаленого репозиторію, не торкаючись робочої гілки. Ви можете переглянути зміни (git log origin/main), перш ніж їх застосовувати.
git rebase: Ця команда бере унікальні локальні коміти (ті, що зробили, але ще не відправили на сервер), "відкладає" їх убік, оновлює локальну гілку до останньої версії з сервера, а потім застосовує локальні коміти один за одним поверх цих оновлень.
Результат: Історія комітів залишається лінійною, без зайвих коммітів злиття. Виглядає так, ніби ви почали свою роботу вже після того, як ваш колега вніс свої зміни. Це “переписує” історію ваших локальних комітів (вони отримують нові хеші).
Аналогія: Ви написали свою частину документа. Потім побачили, що колега вже вніс зміни в основний документ. Ви берете свої правки, “вирізаєте” їх, оновлюєте основний документ до версії колеги, а потім “вставляєте” свої правки вже в оновлену версію.
Команда git pull –rebase є скороченням для послідовності git fetch + git rebase.
git pull
або, якщо ви в гілці tz01 то
git fetch
git merge origin/tz01
Уявимо собі ситуацію, коли ви парцюєте віддалено (з різних причин), але вам необхідно передавати свої зміни в ізольовану мережу клієнта, де в нього стоїть якийсь локальний gitlab чи навіть не локальний, але ви прямого доступу до нього не маєте і можете принести флешку і потім щось з нею робити. А увіть, що ви працюєете постійно над проектом і вам це треба робити реугярно.
Використання патчів — це чудовий спосіб переносити зміни між ізольованими середовищами («air-gapped» чи просто різними мережами), де немає прямого зв’язку між репозиторіями.
В git є два основних підходи: git format-patch (створює текстові файли змін) та git bundle (створює один бінарний файл, який Git сприймає як повноцінний віддалений репозиторій). На постійній основі, bundle набагато зручніший. Потрібно створити один файл, який для Git виглядає як “remote”. Це надійніше, ніж патчі, бо передається вся історія та об’єкти.
Працює це таким чином.
Створити файл, що містить стан вашої гілки (наприклад, develop або main):
1.1. Переше оновлення
git bundle create transport.bundle main
Наступного разу ви зможете створювати інкрементальні бандли (тільки нові коміти), але для початку краще брати всю гілку.
1.2. Наступні оновлення (Інкрементальні)
Щоб не тягати щоразу всю історію (коли проект розростеться), ви зможете створювати бандл лише з новими змінами. Наприклад, від останнього переданого тегу або коміту:
# Створити бандл тільки з новими комітами після версії v1.0
git bundle create incremental.bundle v1.0..main
В обох випадках створиться файл: transport.bundle
Переносити можемо через флешку, поштою, по ssh. Тобто, сам метод доставки не важливий. Важливо, щоб цей файл з’явився на машині в клієнтському перименті.
3.1. Простий процес
Перевірит файл:
git bundle verify transport.bundle
Підтягти зміни прямо з файлу:
git pull transport.bundle main
Коли ви робите git pull з бандлу на клієнтській машині, Git сприймає файл як тимчасове джерело. Якщо ви хочете, щоб клієнтський репозиторій “знав” про ваш локальний як про постійний remote, можна прописати шлях до файлу в конфігурації.
Коли на клієнтській машині ще взагалі немає цього проекту, можна зробити clone прямо з файлу:
git clone transport.bundle my-project
В результаті Git створить папку my-project, розпакує туди всі коміти та налаштує гілку.
3.2. Складний але більш надійний і процесний підхід.
Нагадаю Коли ви робите git pull з бандлу на клієнтській машині, Git сприймає файл як тимчасове джерело. Якщо ви хочете, щоб клієнтський репозиторій “знав” про ваш локальний як про постійний remote, можна прописати шлях до файлу в конфігурації.
3.2.1. Готуємо конфігурацію, для постійного remote, куди будемо класти файли transport.bundle.
git remote add dev-machine /шлях/до/transport.bundle
git fetch dev-machine
Тут треба розуміти, що коли налаштовуємо:
git remote add dev-machine /шлях/до/transport.bundle
Це не видалить уже існуючий origin до GitLab. Просто з’явиться ще одне джерело (alias). Можна побачити обидва, виконавши git remote -v:
origin — буде вказувати на GitLab.
dev-machine — буде вказувати на ваш файл-бандл.
3.2.2. Оновлення bandle
Коли ви приносите новий файл transport.bundle на клієнтську машину і кладете його за тим самим шляхом, вам не потрібно знову додавати remote. Просто отримуємо зміни:
git fetch dev-machine
Git прочитає файл і оновить локальну копію гілки (наприклад, dev-machine/main).
3.2.3. Злиття (Merge) зміни в робочу гілку:
git merge dev-machine/main
3.2.4. Відправка в GitLab
git push origin main
Ідея з “remote як каталог” дуже зручна, тому, що перетворює файл бандлу на повноцінне джерело даних. Git сам відстежує, які коміти є в бандлі, а яких ще немає у вашому проекті. Якщо ви будете постійно переносити зміни, зручно зберігати бандл в одній і тій самій папці з одним і тим самим ім’ям. Тоді команда git fetch dev-machine завжди буде працювати автоматично, як тільки ви заміните старий файл новим.
Використання Python для автоматизації цього процесу — логічний крок, що дозволить уникнути помилок у написанні хешів або тегів вручну.
Ось концепт скрипта, який використовувати як внутрішній інструмент. Цей скрипт автоматично визначає останній створений бандл (або тег) і генерує новий інкрементальний файл.
import subprocess
import datetime
def create_git_bundle(branch="main", last_tag=None):
# Генеруємо ім'я файлу з поточною датою
date_str = datetime.datetime.now().strftime("%Y-%m-%d")
filename = f"transport_{date_str}.bundle"
if last_tag:
# Інкрементальний бандл: від тегу до вершини гілки
command = ["git", "bundle", "create", filename, f"{last_tag}..{branch}"]
else:
# Повний бандл
command = ["git", "bundle", "create", filename, branch]
try:
result = subprocess.run(command, check=True, capture_output=True, text=True)
print(f"✅ Бандл створено: {filename}")
return filename
except subprocess.CalledProcessError as e:
print(f"❌ Помилка: {e.stderr}")
return None
# Використання
# create_git_bundle("main", "v1.0.2")
Мінімізація помилок: Скрипт може сам перевіряти git describe –tags –abbrev=0, щоб знайти останній тег і автоматично підставити його в команду бандлу.
Гнучкість: Ви можете розширити його, щоб він автоматично копіював створений файл на мережевий диск або флешку, щойно вона буде підключена.
Простота: Це перетворює “складну операцію в консолі” на запуск однієї команди python make_bundle.py.
Скрипт робить дві речі: перевіряє цілісність самого файлу (git bundle verify) та звіряє контрольні суми (SHA-256), щоб гарантувати, що антивірус не “відкусив” шматок файлу під час копіювання. Сам скрипт слугує “приймальним шлюзом” для всіх ваших майбутніх бандлів.
import hashlib
import subprocess
import os
def verify_and_fetch(bundle_path, remote_name="dev-machine"):
# 1. Перевірка наявності файлу
if not os.path.exists(bundle_path):
print(f"❌ Файл {bundle_path} не знайдено!")
return
# 2. Перевірка через Git (внутрішня цілісність об'єктів)
print(f"🔍 Перевірка Git-структури {bundle_path}...")
check = subprocess.run(["git", "bundle", "verify", bundle_path],
capture_output=True, text=True)
if check.returncode != 0:
print(f"❌ Git-бандл пошкоджено або не сумісний!\n{check.stderr}")
return
# 3. Оновлення репозиторію
print(f"🚀 Оновлення з {bundle_path}...")
try:
# Оновлюємо remote, який вказує на цей файл
subprocess.run(["git", "fetch", remote_name], check=True)
print("✅ Успішно! Тепер можна виконувати 'git merge dev-machine/main'")
except subprocess.CalledProcessError:
print("❌ Помилка під час git fetch. Перевірте шлях у git remote.")
# Використання
# verify_and_fetch("transport.bundle")
Чому цей скрипт важливий:
Захист від “битих” файлів: Якщо флешка збоїть або антивірус змінить вміст бандлу, скрипт зупинить процес до того, як ви спробуєте влити зміни в основний код.
Завершене архітектурне рішення: Це виглядає як завершене архітектурне рішення від розробки до безпечного постачання (Delivery).
Клієнту не потрібно знати команди Git: Це дає йому скрипт, який каже “Все ОК” або “Файл пошкоджено”.
Це фактично реалізація CI/CD для ізольованих мереж:
Це фактично реалізація CI/CD для ізольованих мереж. На Python, такі скрипти стануть органічним продовженням екосистеми інструментів в парі з аналізом логів в Jupyter notebook.
Для критичних систем рекомендується генерувати окремий файл .sha256 разом із бандлом для зовнішньої перевірки цілісності перед відкриттям у Git
tags: