Чи небезпечний git-svn dcommit після злиття в git?


133

Моя мотивація для випробування git-svn - це легке злиття та розгалуження. Тоді я помітив, що людина git-svn (1) каже:

Запуск Git-Merge або git-pull НЕ рекомендується для гілки, з якої ви плануєте вийти з комісії. Subversion не представляє злиття будь-яким розумним або корисним способом; тож користувачі, які використовують Subversion, не можуть бачити будь-які злиття, які ви здійснили. Крім того, якщо ви зливаєтесь або витягуєте з гіти git, яка є дзеркалом гілки SVN, dcommit може скористатися неправильною гілкою.

Чи означає це, що я не можу створити локальну гілку з svn / trunk (або гілки), зламати, зливатися назад у svn / trunk, а потім dcommit? Я розумію, що користувачі svn побачать той самий безлад, який завжди зливався у svn до 1.5.x, але чи є інші недоліки? Останнє речення мене також хвилює. Чи люди звичайно роблять подібні речі?


14
Це просто неправильно: Subversion не представляє злиття жодним розумним або корисним способом; тож користувачі, які використовують Subversion, не можуть бачити жодних зроблених вами злиттів. Крім того, якщо ви зливаєтесь або витягуєте з гіти git, яка є дзеркалом гілки SVN, dcommit може скористатися неправильною гілкою. Subversion може представляти не тільки інформацію відстеження git merge, але й набагато більш дрібну інформацію. Саме git-svn не вдається скласти належний svn: mergeinfo або зробити dcommit до належної гілки.
Олександр Кітаєв

2
@AlexanderKitaev: документи git-svn змінилися, оскільки додатково існує --mergeinfo=<mergeinfo>опція, яка може передати інформацію про злиття у SVN. Не впевнений, як це слід використовувати.
Mr_and_Mrs_D

Відповіді:


174

Насправді я знайшов ще кращий спосіб із --no-ffваріантом git merge. Вся ця техніка для сквошу, яку я раніше використовував, більше не потрібна.

Мій новий робочий процес зараз такий:

  • У мене є філія «майстер» , який є єдиною галуззю , що я dcommit з і що клон сховища SVN ( -sприпустимо , у вас є стандартне розташування SVN в сховище trunk/, branches/і tags/):

    git svn clone [-s] <svn-url>
    
  • Я працюю над місцевою філією "робота" ( -bстворює гілку "робота")

    git checkout -b work
    
  • взяти участь у відділенні "робота" на місцевому рівні ( -sщоб вийти із повідомлення про фіксацію). У подальшому, я припускаю, ви зробили 3 місцеві коміти

    ...
    (work)$> git commit -s -m "msg 1"
    ...
    (work)$> git commit -s -m "msg 2"
    ...
    (work)$> git commit -s -m "msg 3"
    

Тепер ви хочете скористатися сервером SVN

  • [Врешті-решт] сховайте модифікації, які ви не хочете бачити здійсненими на сервері SVN (часто ви коментували якийсь код у головному файлі лише тому, що хочете прискорити компіляцію та зосередити увагу на заданій функції)

    (work)$> git stash
    
  • відновити головну гілку з сховищем SVN (для оновлення з SVN-сервера)

    (work)$> git checkout master
    (master)$> git svn rebase
    
  • поверніться до робочої гілки та перегляньте майстер

    (master)$> git checkout work
    (work)$> git rebase master
    
  • Переконайтесь, що все добре, наприклад:

    (work)$> git log --graph --oneline --decorate
    
  • Тепер настав час об’єднати всі три комітки з гілки "робота" в "майстер", використовуючи цей чудовий --no-ffваріант

    (work)$> git checkout master
    (master)$> git merge --no-ff work
    
  • Ви можете помітити стан журналів:

    (master)$> git log --graph --oneline --decorate
    * 56a779b (work, master) Merge branch 'work'
    |\  
    | * af6f7ae msg 3
    | * 8750643 msg 2
    | * 08464ae msg 1
    |/  
    * 21e20fa (git-svn) last svn commit
    
  • Тепер ви, мабуть, хочете відредагувати ( amend) останнє посвідчення для ваших учасників SVN (інакше вони побачать лише один комітет із повідомленням "Об'єднати гілку" працювати ""

    (master)$> git commit --amend
    
  • Нарешті здійснюйте SVN-сервер

    (master)$> git svn dcommit
    
  • Поверніться до роботи та відновіть збережені файли:

    (master)$> git checkout work
    (work)$> git stash pop
    

4
Це ТОЧНО робочий потік, який шукав цей git n00b. Створіть локальну гілку, щоб виправити помилку, зробіть що завгодно, а потім надішліть її до svn за допомогою SINGLE. Використовуючи тут найбільш високо оцінену відповідь, зіпсував те, що я робив - не вдалося git svn rebase без помилок.
Жоао Браганча

19
Чи не саме про це застерігають документи, що цитуються у програмі git-svn? Запустивши злиття з --no-ffопцією, ви явно створюєте команду злиття (комісію з двома батьками) замість швидкого перемотування вперед. Щоб усі коміти у відділенні відстеження svn були однопородними, усі злиття повинні бути або швидкими вперед ( --ff-onlyможе допомогти в цьому), або, якщо стовбур змінився за вашою спиною --squash, так?
jemmons

4
Це працює для одноразової гілки, яку ви негайно видаляєте, але git svn dcommitпереписує git-зобов’язання, яке ви їй надаєте. Це означає, що ви втрачаєте іншого батька, і тепер ваш git repo не має жодного запису, в якому ви коли-небудь об'єднували цю гілку master. Це також може залишити ваш робочий каталог у непослідовному стані.
rescdsk

9
використовувати git merge --no-ff work -m "commit message"замість того, щоб мати додатковий git commit --amendкрок
tekumara

3
для мене я вважаю за краще використовувати "git merge --ff-only work", оскільки я хочу зберегти всі свої зобов'язання, а не лише останні
Moataz Elmasry

49

Створення місцевих гілок, безумовно, можливо за допомогою git-svn. Поки ви просто використовуєте локальні гілки для себе і не намагаєтеся використовувати git для злиття між гілками svn вище за течією, вам слід добре.

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

  1. Присвятити все тематичній галузі
  2. git svn rebase (вирішити будь-які конфлікти між вашою роботою та svn)
  3. git checkout master
  4. git svn rebase (це робить наступний крок швидким злиттям вперед, див. коментарі Аарона нижче)
  5. git merge topic_branch
  6. вирішити будь-які конфлікти злиття (на даний момент їх не повинно бути)
  7. git svn dcommit

У мене також є інша ситуація, коли мені потрібно підтримувати деякі локальні зміни (для налагодження), які ніколи не слід підштовхувати до svn. Для цього у мене є вищевказана головна галузь, але також відділення, яке називається "робота", де я зазвичай працюю. Тематичні гілки розгалужуються на роботі. Коли я хочу зайнятися там роботою, я замовлю майстра і використовую вишневий вибір, щоб забрати комітети з робочої гілки, яку я хочу зробити на svn. Це тому, що я хочу уникнути здійснення трьох локальних змін. Потім я випускаю з ведучого відділення і рестаюю все.

git svn dcommit -nСпершу варто запуститись, щоб переконатися, що ви збираєтесь зробити саме те, що маєте намір здійснити. На відміну від git, переписати історію у svn важко!

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


Якщо я правильно це розумію, в git об'єднати git зі svn нормально, але не svn зі svn? Тож я не можу об'єднати svn / гілку зі svn / trunk у git?
Кнут Елдхусет

1
Переписувати історію у SVN важко, якщо ви хочете зробити щось тривіальне. У нетривіальних випадках це практично неможливо.
Арістотель Пагальціс

19
Відповідь Грега Хьюгілла має багато голосів, але я вважаю, що це неправильно. Об'єднання теми_branch з головним є безпечним лише в тому випадку, якщо це просто злиття вперед. Якщо це справжнє злиття, яке вимагає злиття, то комісія злиття втрачається, коли ви виходите з нього. Наступного разу, коли ви спробуєте об'єднати topic_branch у master, git вважає, що першого злиття ніколи не відбулося, і все пекло проривається. Дивіться питання Чому git svn dcommit втрачає історію комісій злиття для місцевих відділень?
Аарон

1
@Greg Hewgill, редагування не ясно. Ви писали, що перезавантаження "змушує наступну фіксацію швидкого злиття вперед". Я не знаю, що це означає, тому що злиття вперед не передбачає здійснення комісії, але, можливо, ви мали на увазі "робить наступне злиття швидким злиттям вперед". Але якщо це ви мали на увазі, це не правильно. Чи є злиття з topic_branch у master злиттям вперед чи ні, залежить від того, чи були зроблені будь-які комітети на master з моменту відгалуження. Коли ви перезавантажуєте master, це створює нові коміти на master, то наступне злиття обов'язково не є швидким вперед об'єднанням.
Аарон

1
@Aaron: Так, я не знаю, що я там думав. Я знову це полагодив, сподіваюся, це має сенс. Одна з неприємностей полягає в тому, що в Git існує стільки способів робити речі, що для опису конкретної послідовності дій потрібне досить певне пояснення .
Грег Хьюгілл

33

Просте рішення: Видаліть гілку "робота" після об'єднання

Коротка відповідь: Ви можете використовувати git як завгодно (див. Нижче простий робочий процес), включаючи злиття. Просто переконайтеся, що слідкуйте за кожною роботою " git merge " та " git branch -d" ", щоб видалити тимчасову роботу.

Основне пояснення: Проблема злиття / dcommit полягає в тому, що щоразу, коли ви «git svn dcommit» гілку, історія з’єднання цієї гілки «сплющується»: git забуває про всі операції з’єднання, що увійшли до цієї гілки: Просто вміст файлу зберігається, але той факт, що цей вміст (частково) походив із певної іншої галузі, втрачається. Див.: Чому git svn dcommit втрачає історію комісій злиття для місцевих відділень?

(Примітка: git-svn може зробити з цим не так багато: svn просто не розуміє набагато більш потужні git злиття. Отже, всередині сховища svn ця інформація про злиття не може бути представлена ​​жодним чином.)

Але це ціле проблема. Якщо ви видалите гілку 'work' після того, як вона була об'єднана в 'master branch', то ваше сховище git на 100% чисте і виглядає точно як ваше сховище svn.

Мій робочий процес: Звичайно, я спершу клонував віддалений сховище svn у локальне сховище git (це може зайняти деякий час):

$> git svn clone <svn-repository-url> <local-directory>

Вся робота тоді відбувається всередині "local-каталогу". Щоразу, коли мені потрібно отримувати оновлення з сервера (наприклад, "svn update"), я роблю:

$> git checkout master
$> git svn rebase

Я всю свою розробку працюю в окремій галузі "робота", яка створена так:

$> git checkout -b work

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

$> git commit -am '-- finished a little piece of work'

Наступний крок (git rebase -i) - необов’язковий --- це просто очищення історії перед архівуванням на svn: Одного разу я дістався стійкого кілометражу, який я хочу поділитися з іншими, я переписав історію цього "твору" гілка та очищення повідомлень про фіксацію (іншим розробникам не потрібно бачити всі маленькі кроки та помилки, які я зробив на шляху --- лише результат). Для цього я і роблю

$> git log

і скопіюйте хеш sha-1 останнього комітету, який знаходиться у прямому сховищі svn (як зазначено git-svn-id). Тоді я дзвоню

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

Просто вставте ша-1 хеш нашого останнього свн-файлу замість мого. Ви можете прочитати документацію з "git help rebase" для деталей. Коротше кажучи: ця команда спочатку відкриває редактор, представляючи ваші зобов’язання ---- просто змініть "pick" на "squash" для всіх тих комітетів, які ви хочете скасувати з попередніми комітами. Звичайно, перший рядок повинен залишатися як "вибір". Таким чином, ви можете конденсувати свої численні дрібниці в одній або декількох значущих одиницях. Збережіть та вийдіть із редактора. Ви отримаєте інший редактор з проханням переписати повідомлення журналу фіксації.

Коротше кажучи: після того, як я закінчу "злом коду", я масажую свою "роботу" гілку, поки не з’явиться, як я хочу представити її іншим програмістам (або як я хочу побачити роботу за кілька тижнів, коли я переглядаю історію) .

Для того, щоб змінити зміни до сховища svn, я:

$> git checkout master
$> git svn rebase

Тепер ми повернулися до старої гілки 'master', оновленої всіма змінами, що відбулися в середній час у сховищі svn (ваші нові зміни приховані у відділенні 'work').

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

$> git checkout master
$> git merge work        # (1) merge your 'work' into 'master'
$> git branch -d work    # (2) remove the work branch immediately after merging
$> git svn dcommit       # (3) push your changes to the svn repository

Примітка 1: Команда "git branch -d work" є досить безпечною: вона дозволяє лише видалити гілки, які вам більше не потрібні (тому що вони вже об'єднані у вашу поточну гілку). Якщо ви виконаєте цю команду помилково, перш ніж об’єднати свою роботу з гілкою 'master', ви отримаєте повідомлення про помилку.

Примітка 2: Переконайтесь, що ви видалите свою гілку з 'git branch -d робота' між об'єднанням і dcommit: Якщо ви спробуєте видалити гілку після dcommit, ви отримаєте повідомлення про помилку: Коли ви робите 'git svn dcommit', git забуває про це ваша філія була об'єднана з "господарем". Ви повинні видалити його за допомогою "git branch -D work", яке не робить перевірку безпеки.

Тепер я одразу створю нову гілку "робота", щоб випадково не зламати "гілку":

$> git checkout -b work
$> git branch            # show my branches:
  master
* work

Інтеграція вашої 'роботи' зі змінами на svn: Ось, що я роблю, коли 'git svn rebase' виявляє, що інші змінили сховище svn, коли я працював над моєю гілкою 'work':

$> git checkout master
$> git svn rebase              # 'svn pull' changes
$> git checkout work           # go to my work
$> git checkout -b integration # make a copy of the branch
$> git merge master            # integrate my changes with theirs
$> ... check/fix/debug ...
$> ... rewrite history with rebase -i if needed

$> git checkout master         # try again to push my changes
$> git svn rebase              # hopefully no further changes to merge
$> git merge integration       # (1) merge your work with theirs
$> git branch -d work          # (2) remove branches that are merged
$> git branch -d integration   # (2) remove branches that are merged
$> git svn dcommit             # (3) push your changes to the svn repository

Є більш потужні рішення: представлений робочий процес спрощений: він використовує повноваження git лише в кожному раунді "update / hack / dcommit" ---, але залишає історію довгострокової роботи так само лінійною, як і сховище svn. Це нормально, якщо ви просто хочете почати використовувати git merges маленькими першими кроками у спадковому svn-проекті.

Коли ви ознайомитесь із об'єднанням git, сміливо вивчайте інші робочі процеси: якщо ви знаєте, що ви робите, ви можете змішати git merges зі svn merges ( Використовуючи git-svn (або подібне) просто для того, щоб допомогти зі злиттям svn? )


Це здається зайвим складним. Я чув, як люди роблять git merge --squash work, чому б цього не зробити? Я бачу, як виконуються сквош-коміти у гілці перед об'єднанням, якщо ви створюєте більше одного "вибору" (скажімо, у вас є 8 комітів, і ви перетворюєте кожен з 4-х комітів у 1 і об'єднуєте 2-х команд у головний). Крім того, коли я rebase
оновлюю

8

Відповідь Грега Хьюгілла зверху не є безпечною! Якщо якісь нові коміти з'явилися на магістралі між двома "git svn rebase", злиття не буде швидким вперед.

Це можна забезпечити, використовуючи прапор "--ff-only" для git-merge, але я зазвичай не запускаю "git svn rebase" у гілці, а лише "git rebase master" на ньому (припустимо, що це лише локальний відділення). Тоді після цього гарантовано буде швидко перейти вперед.


6

Безпечним способом об’єднання svn гілок в git є використання git merge --squash. Це створить єдиний фіксатор і зупинить вам додавання повідомлення.

Скажімо, у вас є тема svn гілка, яка називається svn-гілка.

git svn fetch
git checkout remotes/trunk -b big-merge
git merge --squash svn-branch

в цей момент у вас є всі зміни від svn-гілки, розбитого на один коміт, що чекає в індексі

git commit

Як зазначають інші, однак це втрачає деталізацію комітетів.
Kzqai

@Tchalvak: Іноді ти хочеш саме цього. Я часто вчиняє у філії функції ала "фіксований безлад від попереднього комітету", і ці тупики я люблю ховати через гарну репутацію :-)
schoetbi

1
Так, хоча я вважаю, що це прогулянка по канату, оскільки щоразу, коли ти робиш сквош, ти також втрачаєш здатність розлучати окремі зобов’язання пізніше. Справа лише в різних варіантах дотримання стандартів.
Kzqai

1
@schoetbi так, іноді вам потрібно очистити брудну історію перед публікацією. Ось тут 'git rebase -i <trunk>' є чудовою підмогою: ви можете приєднатися / змінити порядок / вилучити і навіть розділити (!) Фіксацію, виправити msg вибірково.
інгер

5

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

git rebase master topic

який відтворюватиме ваші зобов’язання над головним відділенням, готовим до виходу з комісії

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.