Копіювати файл і всю його історію

Я та інший розробник розробляють API, доступ до якого здійснюється за допомогою іншого коду. Оскільки ми змінюємо поведінку API, щоб максимально відповідати нашим потребам, ми випускаємо додаткові версії API, не відмовляючись від старих версій, так що існуючі додатки, що використовують API, не потрібно буде негайно оновлювати. Наприклад:

$ cat 0.5.php
<?php
$username = $_POST['name'];
?>

$ cat 0.6.php
<?php
$username = $_POST['username'];
?>

When we start a new version, typically we will cp version N-1.php to N.php and code from there. However, if we do this with Git, then we lose the entire blame, diff, and other histories from the file for comparison and reverting. How can I 'forge' this history of the old file into the new file such that blame, log, diff, and such commands "just work" without presenting to them additional flags or arguments such as --follow?

4
Ідея полягає в тому, що декілька версій API будуть доступні користувачам одночасно. Я не маю користувачів SSH SSH на своєму сервері та перевіряю копію відповідно до версії API, яка їм потрібна.
додано Автор dotancohen, джерело
Можливо, я тут нібито, але це не точка Git для обробки ваших версій? Так що вам не потрібно вдаватися до таких речей, як "змінити назви файлів" або "копіювати історію"?
додано Автор Madara Uchiha, джерело
Це можна зробити за допомогою гілок, наприклад, джерело PHP.
додано Автор Madara Uchiha, джерело

6 Відповіді

Ви хочете використовувати прапорець -C . Це буде виявляти копії, а також перейменовувати, так що він може слідувати історії. diff , guilty і log прийняти цей прапор.

Хоча, як @ madara-uchiha сказав: ви повинні дивитися на використання тегів, і, можливо, генерувати їх з файлів git-x.y . Ви можете використовувати щось на зразок наступного, щоб витягнути константи файла у певному тезі:

git show v0.6:git.php > git-0.6.php

Де v0.6 - це тег, який вас цікавить.

Update:

Ось невеликий скрипт для цього. Цей спочатку передбачає, що ваші теги мають форму x.y або x.y.z :

#!/bin/bash
versions=$(git tag -l | egrep -o '^[0-9]+\.[0-9]+(\.[0-9]+)?$')
for version in $versions
do
    git show $version:git.php > git-$version.php
done

Якщо ви теги мають форму vX.Y або vX.YZ , і ви хочете, щоб git-xyphp або git- xyzphp як ім'я файлу, це працює:

#!/bin/bash
versions=$(git tag -l | egrep -o '^v[0-9]+\.[0-9]+(\.[0-9]+)?$')
for version in $versions
do
    git show $version:git.php > git-${version/#v/}.php
done

Run this script as part of your release process, and it will generate all the versions for you. Also, it's pretty easy to drop the git- from the name. For example, > git-$version.php becomes > $version.php.

10
додано
Спасибі, але я шукаю рішення, яке буде працювати, не додаючи додаткових прапорців до вину , diff , log та інших команд Git . Я роз'яснив це питання і додав щедрість.
додано Автор dotancohen, джерело
Спасибі, це розумна ідея, що ви маєте файл git.php, який працює у файлі 'dev', який може бути розбитий на звинувачення, тощо, а потім розгалуженим. На жаль, схема не може бути зворотньо додана до проекту, але я розгляну це як можливе рішення, переглянувши деякі інші ідеї, представлені тут. Дякую!
додано Автор dotancohen, джерело
Gіt не має можливості копіювати та стежити за історією без додаткових прапорів. Механізму просто не існує. Це "функція" в Gіt, яка дозволяє не явно повідомляти ходи або копії на Gіt (це просто фігурує це після факту). Недоліком є ​​те, що, оскільки він використовує евристику для визначення цього, функція за замовчуванням вимикається, оскільки це може зайняти багато часу в залежності від виконання зобов'язань. Я думаю, вам краще всього вказати ім'я вашого файлу git.php у сховищі та створити файли з версій git.php з тегів. Тоді вам не потрібні варіанти, щоб побачити всю історію.
додано Автор John Szakmeister, джерело
Для анотації ви можете вказати опцію -C один раз або три рази, вона буде шукати копії з інших файлів. Докладніше в анотації git help .
додано Автор kan, джерело

Я думаю, ви трохи затьмарюєте це, тому що здається, що ви намагаєтесь поєднати те, як керування версіями обробляє речі тим, яким виявляється API (наприклад, як веб-сервер обробляє речі).

Щоб декілька версій API працювали одночасно, споживачеві, імовірно, потрібно вказати версію, яку вони хочуть використовувати для певного виклику. Для цілей даної відповіді я вважаю, що ви працюєте аналогічним чином з API Stack Exchange, так що ця версія вказується як перший компонент "каталогу" URL-адреси API (наприклад, для версії 1.5 я направляю моє запит на http: //domain.tld/1.5/call , версія 1.6, я використовую http: //domain/1.6/? method = call , і т.д. тощо). Але насправді цей елемент не має значення, якщо у вас є певний механізм для визначення відповідної версії та маршрутизації запиту на правильний контролер на рівні веб-сервера.

Контроль версії

Підхід, який я хотів би взяти тут, досить простий. Кожна версія має власну гілку в сховищі. Будь-яка робота з розробкою, виконаною з цією версією, виконується у відгалуженні від відділення версії або здійснюється безпосередньо до версії. Майстер завжди містить останню стабільну версію.

Наприклад, припустимо, що нинішній реліз становить 1,5, і все в даний час є майстром, і у вас немає історичних гілок. Намалюйте рядок під поточним стабільним кодом і створіть гілку під назвою 1.5. Тепер, щоб почати розробку на 1.6, який будуватиметься на гілці 1.5, створіть нову гілку від майстра і назови її 1.6.

Будь-який розробник, який працює на 1.6, відбувається у розділі 1.6 або в інших галузях, створених з використанням 1.6 в якості бази. Це означає, що все може бути приємним і чисто натиснути/витягнути у відділення 1.6, якщо необхідно.

Якщо вам потрібно застосувати невелике виправлення до версії 1.5, ви можете легко зробити це у розділі 1.5. Якщо ви хочете витягнути з філії філію 1.6, то вам потрібно буде "вишневий" - тому що гілки почали розходитися, будь-які подібні проблеми повинні розглядатися вручну, щоб максимально безпечно захистити "стабільну" "кодова база".

Коли настав час створити 1.7/2.0/що завгодно, перетягніть версію 1.6 на майстер, позначте його та створіть нову гілку для нової версії.

Таким чином, повна історія того, хто зробив те, що і коли для кожної версії/випуску зберігається в гілках. Як згадували інші, не забудьте позначити вашу версію.

Веб-сервер

За допомогою наведеного вище підходу налаштування веб-сервера є досить тривіальним для підтримки. Корінь кожного випуску просто синхронізується з відповідною гілкою.

Отже, для простоти, давайте уявімо, що кореневий каталог сховища в елементі керування версіями відповідає коренем документа коду API (насправді це навряд чи буде, але трохи перезапису URL-адрес або подібних підходів можна вирішити). це).

У кореневому документі для домену на веб-сервері ми створюємо таку структуру каталогів:

    |
    |--- 1.5
    |
    |--- 1.6

У кожному з каталогів 1.5, 1.6 ми клонуємо сховище від централізованого керування версіями та перемикаємося на відповідну гілку. Кожного разу, коли ви хочете натиснути зміну в режимі реального часу, просто перетягніть ці зміни з елемента керування версіями у відповідну гілку.

У середовищі високого обсягу ви можете мати цілий сервер, присвячений обслуговуванню кожної версії з ідентифікатором версії як субдомену, але застосовується той же загальний принцип, за винятком того, що сховище можна клонувати безпосередньо в кореневий документ кожного сервера.

Багато (якщо не всі) процесу створення каталогів для нових гілок, клонування репо в ньому та перемикання на відповідну гілку, а також вилучення патчів/виправлень для випусків можуть бути автоматизовані за допомогою скриптів/cron та ін., Але перш ніж зробити це, не забудьте: натискання змін на живий сервер без участі людини часто закінчується сльозами.


Альтернативний підхід

... було б створити єдиний батьківський репозиторій, який виконує роль root root для домену. У цьому ви створюєте підмодули в корені сховища для кожної версії. Загальний ефект, який це створить, буде дуже подібним, але має "перевагу" лише синхронізацію одного сховища на сервері та збереження структури каталогів веб-сервера, визначеної контролем версії. Проте, особисто мені не подобається такий підхід, з кількох причин:

  • Сумодули - це біль, який потрібно підтримувати. Вони прикріплені до певної фіксації, і це легко забути.
  • Я вважаю, що контроль, наданий галузевим підходом, більш грамотний і чіткіший щодо того, що відбувається.

Я погоджуюся з тим, що обидва ці причини, в основному, є особистими перевагами, тому я можу це зробити.

6
додано

ПОПЕРЕДЖЕННЯ. Наступна команда переписує історію , яка найчастіше небажана  на спільних сховищах. Подумайте, перш ніж натиснути -f.

Перепишіть свою історію, щоб додати другу копію файлу за допомогою git filter-branch :

git filter-branch --tree-filter 'if [ -f 0.5.php ]; then cp 0.5.php 0.6.php; fi' HEAD

Тепер 0.6.php є EXACT дублікатом 0.5.php над усією історією.


Тоді вашому співробітнику потрібно опрацювати нову історію.

Як я можу відновити/синхронізувати після того, як хтось натискає перезавантаження чи скинути до опублікованої гілки?

2
додано

Це глухий хак, але, здається, наближається до бажаної поведінки. Зауважте, він передбачає, що вас позначено якнайшвидше здійснення 0.5.php, яке вас цікавить як first :

  1. branch

    % git checkout -b tmp

  2. make a patch folder and patch versions of your 0.5.php file's commit history

    % mkdir patches && git format-patch first 0.5.php -o patches

  3. delete your file and checkout the first copy of it

    % rm 0.5.php && git checkout first -- 0.5.php

  4. rename your file

    % mv 0.5.php 0.6.php

  5. tweak your patch files to use the new name

    % sed 's/0\.5\.php/0\.6\.php/g' -i patches/0*

  6. commit (if you haven't already a couple of times)

    % git add -A && git commit -m'ready for history transfer'

  7. apply the patches

    % git am -s patches/0*

  8. go back to master, pull the new file over and delete the tmp branch

    % git co master && git co tmp -- 0.6.php && git branch -D tmp

Вуала! У вас є файл 0.6.php має історію, яка повторяє ваш файл 0.5.php, за винятком випадків, коли кожен фіксований в історії 0.6.php матиме унікальний ідентифікатор від тих, що містяться в історії 0.5.php. Час і провину має бути правильним. З невеликим додатковим зусиллям, ви, напевно, можете помістити все це в сценарії, а потім псевдонім скрипта до git cp .

2
додано
Дякую, якщо я можу отримати цю роботу, то це найкраще рішення. Тим не менш, я не можу отримати його на роботу! Припускаючи, що я позначаю кожен фікс, коли я використовую початковий тег commit, то в новому файлі немає історії старого файлу. Коли я використовую поточний тег, файл патча не створюється.
додано Автор dotancohen, джерело
Я не можу отримати відповіді на роботу, але це виглядає найбільш перспективним. Таким чином, я нагороджую щедрість тут. Дякую!
додано Автор dotancohen, джерело
Вам не потрібно буде позначати кожен фікс. Єдиний тег, який я використовував у наведеному вище прикладі, - це забезпечити зручну обробку першої/найстарішої копії в історії, яку я хотів скопіювати. Використання поточної корекції не створить патча, тому що зміни не відбуваються. Це ваш поточний (зареєстрований) стан. Коли ви використовували найстарішу копію в історії свого файлу, і ви дійшли до кроку 6, чи є у вас підкаталог патчів із рядом патч-файлів?
додано Автор Alnilam, джерело

Більш стандартний спосіб полягає в тому, щоб мати лише один файл api.php , а також використовувати гілки та теги для позначення нових версій.

Що стосується обслуговування файлів: якщо ви хочете запропонувати своїм користувачам кілька варіантів API, скористайтеся деяким процесом розгортання для перевірки та створення конкретних версій вашого API, перейменування та переміщення за вашим бажанням та встановлення загального доступу до цього - не до ваше дерево дерева.

1
додано

Перегляньте піддерево GIT ( також тут ). За допомогою цього ви зможете розділити частину історії на цей файл. Ви також можете його дублювати, якщо інший користувач використовує інтерактивний режим. Тоді ви можете об'єднати його назад і мати дублікат.

1
додано
Дякую. Мені здається, що відсутній "чарівний клей", щоб отримати скопійований файл у головну гілку. Я намагаюся використовувати основну гілку як підмодуль самого для копіювання файлу. Якщо це не те, що ви мали на увазі, то я хотів би знати, що ви маєте на увазі. Дякую.
додано Автор dotancohen, джерело