Является ли 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 pre 1.5.x, но есть ли другие недостатки? Последнее предложение меня тоже беспокоит. Люди обычно делают такие вещи?

Кнут Элдхусет
источник
14
Это просто неправильно: Subversion не представляет слияния каким-либо разумным или полезным способом; таким образом, пользователи, использующие Subversion, не могут видеть сделанные вами слияния. Более того, если вы объединяете или извлекаете ветку git, которая является зеркалом ветки SVN, dcommit может зафиксировать неправильную ветку. Subversion может представлять не только информацию отслеживания git-слияний, но и гораздо более детальную информацию. Это 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-парней (в противном случае они увидят только один коммит с сообщением "Merge branch 'work'"

    (master)$> git commit --amend
    
  • Наконец зафиксировать на сервере SVN

    (master)$> git svn dcommit
    
  • Вернитесь к работе и в конечном итоге восстановите ваши спрятанные файлы:

    (master)$> git checkout work
    (work)$> git stash pop
    
Себастьян Варретт
источник
4
Это точно рабочий процесс, который искал этот git n00b. Создайте локальную ветку, чтобы исправить ошибку, сделайте что угодно, затем отправьте ее в svn с ОДНОЙ фиксацией. Использование ответа с самым высоким рейтингом здесь испортило то, что я делал - не смог git svn rebase без ошибок.
Жоао Браганса
19
Разве это не то, что предостерегают git-svn docs, процитированные в op? Запустив слияние с --no-ffопцией, вы явно создаете коммит слияния (коммит с двумя родителями) вместо ускоренной перемотки вперед. Чтобы гарантировать, что все коммиты в ветви svn-tracking являются коммитами с одним родителем, все слияния должны быть либо ускоренными ( --ff-onlyможет помочь в этом), либо, если транк изменился за вашей спиной, а --squash, верно?
Джеммонс
4
Это работает для одноразовой ветки, которую вы немедленно удаляете, но git svn dcommitпереписывает git commit, который вы ей даете. Это означает, что вы теряете своего другого родителя, и теперь в вашем git-репо нет записи, в которую вы когда-либо объединяли эту ветку 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 верхнего уровня, все будет в порядке.

У меня есть «главная» ветка, которую я использую для отслеживания сервера SVN. Это единственная ветка, из которой я делаю коммит. Если я делаю какую-то работу, я создаю ветку тем и работаю над ней. Когда я хочу зафиксировать это, я делаю следующее:

  1. Зафиксируйте все в ветке темы
  2. git svn rebase (разрешите любые конфликты между вашей работой и svn)
  3. мастер проверки
  4. git svn rebase (это делает следующий шаг быстрым слиянием, см. комментарии Аарона ниже)
  5. git merge topic_branch
  6. разрешить любые конфликты слияния (в этот момент их не должно быть)
  7. git svn dcommit

У меня также есть другая ситуация, когда мне нужно сохранить некоторые локальные изменения (для отладки), которые никогда не должны передаваться в SVN. Для этого у меня есть вышеупомянутая основная ветка, но также есть ветка под названием «работа», где я обычно работаю. Тематические ветки разветвлены от работы. Когда я хочу зафиксировать работу там, я извлекаю master и использую cherry-pick, чтобы выбрать коммиты из рабочей ветки, которые я хочу зафиксировать в svn. Это потому, что я хочу избежать фиксации трех локальных изменений. Затем я делаю коммит из ветки master и перебазирую все.

Стоит побежать git svn dcommit -nсначала, чтобы убедиться, что вы собираетесь совершить именно то, что намереваетесь совершить. В отличие от git, переписывать историю в svn сложно!

Я чувствую, что должен быть лучший способ объединить изменения в ветке темы, пропуская эти локальные изменения изменений, чем использование cherry-pick, поэтому, если у кого-то есть какие-либо идеи, они будут приветствоваться.

Грег Хьюгилл
источник
Если я правильно понимаю, в git слияние git с svn - это нормально, но не svn с svn? Так что я не могу объединить мою SVN / ветку с SVN / trunk в GIT?
Кнут Элдхусет
1
Переписывать историю в SVN сложно, если вы хотите сделать что-то тривиальное. В нетривиальных случаях это практически невозможно.
Аристотель Пагальцис
19
У ответа Грега Хьюгилла много голосов, но я считаю, что это неправильно. Объединение из topic_branch в master безопасно только в том случае, если это просто ускоренное слияние. Если это реальное слияние, требующее коммита слияния, то коммит слияния будет потерян, когда вы будете коммитить. В следующий раз, когда вы попытаетесь объединить topic_branch с master, git думает, что первое слияние никогда не произошло, и весь ад проваливается. См. Вопрос Почему git svn dcommit теряет историю коммитов слияния для локальных веток?
Аарон
1
@ Грег Хьюгилл, редактирование не ясно. Вы писали, что rebase «делает следующий коммит слиянием вперед». Я не знаю, что это значит, потому что быстрое слияние не требует фиксации, но, возможно, вы имели в виду «делает следующее слияние быстрым слиянием». Но если это то, что вы имели в виду, это не правильно. Является ли слияние topic_branch с master быстрым слиянием или нет, зависит от того, были ли выполнены какие-либо фиксации на master с момента перехода. Когда вы перебазироваться мастер, который создает новые коммиты на хозяине, поэтому последующее слияние обязательно не быстро вперед слияния.
Аарон
1
@ Аарон: Да, я не знаю, о чем я там думал. Я исправил это снова, надеюсь, это имеет смысл. Одна из проблем заключается в том, что в Git так много способов сделать что-то, что для описания определенной последовательности действий требуется какое-то объяснение .
Грег Хьюгилл
33

Простое решение: удалить ветку 'работа' после слияния

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

Фоновое объяснение: проблема слияния / dcommit заключается в том, что всякий раз, когда вы 'git svn dcommit' ветвь, история слияния этой ветки 'сплющивается': git забывает обо всех операциях слияния, которые были в этой ветке: сохраняется только содержимое файла, но тот факт, что этот контент (частично) пришел из определенной другой ветки, теряется. Смотрите: Почему git svn dcommit теряет историю коммитов слияния для локальных веток?

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

Но это все проблема. Если вы удалите ветку 'work' после того, как она будет объединена с 'master веткой', то ваш репозиторий git будет на 100% чист и выглядит точно так же, как ваш svn-репозиторий.

Мой рабочий процесс: Конечно, я сначала клонировал удаленный svn-репозиторий в локальный git-репозиторий (это может занять некоторое время):

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

Вся работа происходит внутри «локального каталога». Всякий раз, когда мне нужно получить обновления с сервера (например, '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

Просто вставьте хэш sha-1 нашего последнего SVN-коммита вместо моего. Вы можете прочитать документацию с помощью 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 work' между слиянием и dcommit: если вы попытаетесь удалить ветку после dcommit, вы получите сообщение об ошибке: Когда вы делаете 'git svn dcommit', git забывает, что ваша ветвь была объединена с 'master'. Вы должны удалить его с помощью 'git branch -D work', который не выполняет проверку безопасности.

Теперь я немедленно создаю новую ветку 'работа', чтобы избежать случайного взлома ветки 'master':

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

Интеграция вашей «работы» с изменениями в svn: Вот что я делаю, когда «git svn rebase» показывает, что другие изменили репозиторий svn, когда я работал над моей веткой «работа»:

$> 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 merging, не стесняйтесь исследовать другие рабочие процессы: если вы знаете, что делаете, вы можете смешивать git merges с svn merges ( используя git-svn (или аналогичный) просто для того, чтобы помочь с svn merge? )

Яаков Белч
источник
Это кажется излишне сложным. Я слышал, что люди делают git merge --squash work, почему бы не сделать это? Я могу видеть выполнение коммитов сквоша в ветви перед слиянием, если вы создаете более одного «пика» (скажем, у вас есть 8 коммитов, и вы превращаете каждый из 4 коммитов в 1, а слияние 2 коммитов - с мастером). Также, когда я обновляю свою ветку 'работа', я делаю rebaseэто проще, чем создание другой ветки для моей ветки и выполнение слияний ...
void.pointer
8

Ответ Грега Хьюгилла сверху небезопасен! Если между двумя «git svn rebase» в транке появятся какие-либо новые коммиты, слияние не будет происходить быстро.

Это может быть обеспечено использованием флага «--ff-only» для git-merge, но я обычно не запускаю «git svn rebase» в ветке, а только «git rebase master» (предполагая, что это только локальный филиал). Тогда после этого «git merge thebranch» будет гарантированно ускорен.

Мариус К
источник
6

Безопасный способ объединить svn-ветки в git - это использовать git merge --squash. Это создаст единый коммит и остановит вас, чтобы добавить сообщение.

Допустим, у вас есть тема svn branch, которая называется svn-branch.

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

к этому моменту все изменения из svn-ветви сведены в один коммит, ожидающий в индексе

git commit
luntain
источник
Однако, как отмечали другие, это теряет гранулярность коммитов.
Kzqai
@ Чалвак: Иногда ты хочешь именно этого. Я часто делаю коммиты в ветке функций аля "исправленный беспорядок с предыдущего коммита" и эти тупики я люблю прятать из-за хорошей репутации :-)
schoetbi
1
Да, хотя я считаю это ходьбой по канату, так как всякий раз, когда вы раздавливаете, вы также теряете способность разбирать отдельные коммиты позже. Просто вопрос разных вариантов принятия стандартов.
Kzqai
1
@schoetbi Да, иногда вам нужно очистить грязную историю перед публикацией. Вот где 'git rebase -i <trunk>' - отличная помощь: вы можете присоединиться / переупорядочить / удалить и даже разделить (!) Коммиты, выборочно исправить сообщение.
Звоните
5

Перебазируйте локальную ветку git на главную ветку git, затем dcommit, и, таким образом, похоже, что вы сделали все эти коммиты последовательно, чтобы svn люди могли видеть это линейно, как они привыкли. Итак, если у вас есть локальная ветка с названием topic, вы можете сделать

git rebase master topic

который затем воспроизведет ваши коммиты через главную ветку, готовую для вас

JoeyJ
источник