Временно отложите незафиксированные изменения в Subversion (а-ля "git-stash")

При программировании программного обеспечения, хранящегося в репозитории Subversion, я часто изменяю некоторые файлы, а затем замечаю, что хотел бы внести некоторые подготовительные изменения в свою основную работу. Например, при реализации новой функциональности я замечаю некоторый рефакторинг, который может мне помочь.

Чтобы не смешивать два несвязанных изменения, в этих случаях я хотел бы "убрать" мои изменения, то есть вернуться к версии репозитория, сделать некоторые другие изменения, зафиксировать их, а затем "извлечь" мои изменения.

git-stash позволяет делать именно это. Есть ли способ сделать это с помощью Subversion, напрямую или с помощью какого-либо плагина или скрипта. Плагины Eclipse тоже подойдут.

17 ответов

Решение

Когда я получил незафиксированные изменения от одной задачи в моей рабочей копии, и мне нужно переключиться на другую задачу, я делаю одну из двух вещей:

  1. Проверьте новую рабочую копию для второго задания.

    или же

  2. Начать ветку:

    workingcopy$ svn copy CURRENT_URL_OF_WORKING_COPY SOME_BRANCH
    workingcopy$ svn switch SOME_BRANCH
    workingcopy$ svn commit -m "work in progress"
    workingcoyp$ svn switch WHATEVER_I_WAS_WORKING_ON_BEFORE
    

У меня есть несколько сценариев, которые помогают автоматизировать это.

Этот блог советует использовать diff и patch.

  • git stash примерно становится svn diff > patch_name.patch; svn revert -R .
  • git stash apply становится patch -p0 < patch_name.patch

Обратите внимание, что это не хранит изменения метаданных или (я думаю) каталог создает / удаляет. (Да, svn отслеживает их отдельно от содержимого каталога, в отличие от git.)

Вы можете сохранить свои текущие изменения с svn diff в файл патча, затем верните свою рабочую копию:

svn diff > stash.patch
svn revert -R .

После того, как вы реализовали свою подготовительную функцию, вы можете применить ваш патч с помощью утилиты:

patch < stash.patch

Как отметили другие, это не будет работать с svn:properties и древовидные операции (добавление, удаление, переименование файлов и каталогов).

Бинарные файлы также могут создавать проблемы, я не знаю, как исправление (или TortoiseSVN в этом случае их обрабатывает).

Самый простой способ - использовать временную ветку, например:

$ svn copy ^/trunk ^/branches/tempbranch
$ svn switch ^/branches/tempbranch
$ svn commit -m "Stashed"
$ svn switch ^/trunk
$ ... hack away in trunk ...
$ svn commit -m "..."
$ svn merge ^/branches/tempbranch .
$ svn rm ^/branches/tempbranch
$ ... continue hacking

Это может (и, вероятно, должно) быть помещено в сценарий, если это делается на более регулярной основе.

По состоянию на 2018-04-13 (Subversion 1.10.0) у вас есть экспериментальный svn shelve командование( TortoiseSVN поддерживает команду)

Начиная с 1.10.0, это не что иное, как помощник для сохранения патча и применения обратно, поэтому он имеет те же ограничения, что и svn diff + patch (т.е. не может обрабатывать двоичные файлы и переименовывать). (Edit: похоже, что бинарная поддержка будет в следующей версии)

Редактировать ^2: С 1.11.0 (выпущен 2018-10-30), двоичные файлы поддерживаются. Переставленные переименованные файлы остались без поддержки. Стеллажи в 1.11 несовместимы с полками, созданными в 1.10.

Заметки о дизайне можно найти на их вики.

$ svn x-shelve --help
x-shelve: Move local changes onto a shelf.
usage: x-shelve [--keep-local] SHELF [PATH...]

  Save the local changes in the given PATHs to a new or existing SHELF.
  Revert those changes from the WC unless '--keep-local' is given.
  The shelf's log message can be set with -m, -F, etc.

  'svn shelve --keep-local' is the same as 'svn shelf-save'.

  The kinds of change you can shelve are committable changes to files and
  properties, except the following kinds which are not yet supported:
     * copies and moves
     * mkdir and rmdir
  Uncommittable states such as conflicts, unversioned and missing cannot
  be shelved.

  To bring back shelved changes, use 'svn unshelve SHELF'.

  Shelves are currently stored under <WC>/.svn/experimental/shelves/ .
  (In Subversion 1.10, shelves were stored under <WC>/.svn/shelves/ as
  patch files. To recover a shelf created by 1.10, either use a 1.10
  client to find and unshelve it, or find the patch file and use any
  1.10 or later 'svn patch' to apply it.)

  The shelving feature is EXPERIMENTAL. This command is likely to change
  in the next release, and there is no promise of backward compatibility.

Valid options:
  -q [--quiet]             : print nothing, or only summary information
  --dry-run                : try operation but make no changes
  --keep-local             : keep path in working copy

(...)

$ svn x-unshelve --help
x-unshelve: Copy shelved changes back into the WC.
usage: x-unshelve [--drop] [SHELF [VERSION]]

  Apply the changes stored in SHELF to the working copy.
  SHELF defaults to the newest shelf.

  Apply the newest version of the shelf, by default. If VERSION is
  specified, apply that version and discard all versions newer than that.
  In any case, retain the unshelved version and versions older than that
  (unless --drop is specified).

  With --drop, delete the entire shelf (like 'svn shelf-drop') after
  successfully unshelving with no conflicts.

  The working files involved should be in a clean, unmodified state
  before using this command. To roll back to an older version of the
  shelf, first ensure any current working changes are removed, such as
  by shelving or reverting them, and then unshelve the desired version.

  Unshelve normally refuses to apply any changes if any path involved is
  already modified (or has any other abnormal status) in the WC. With
  --force, it does not check and may error out and/or produce partial or
  unexpected results.

  The shelving feature is EXPERIMENTAL. This command is likely to change
  in the next release, and there is no promise of backward compatibility.

Valid options:
  --drop                   : drop shelf after successful unshelve
(...)

$ svn help | grep x-
 x-shelf-diff
 x-shelf-drop
 x-shelf-list (x-shelves)
 x-shelf-list-by-paths
 x-shelf-log
 x-shelf-save
 x-shelve
 x-unshelve

Я не знаю простого способа сделать это с помощью SVN. Честно говоря, я бы посоветовал использовать git-svn сделать git репо, который действует как рабочая копия svn, и просто используя git stash с этим. Просто замени git pull с git svn rebase а также git push с git svn dcommit и вы можете фактически сохранить 90% своего рабочего процесса git и по-прежнему общаться с сервером SVN.

Существует небольшой скрипт на Python 2, который называется svn-stash доступно по лицензии GPL 3: https://github.com/frankcortes/svn-stash.

Это работает как svn diff/patch упомянутые решения и предлагает толкать и выталкивать изменения как diff в некоторый локальный каталог. К сожалению, тайники не могут быть названы, и может быть выдан только последний (ну, да, это стек, но нет реальной причины для такого ограничения.) Но тогда вы всегда можете встроить отсутствующие функции в источник.

Он написан для *ix, но после замены каждого "/" на os.sep это хорошо работает и под Windows.

Если вы используете SVN 1,7 или выше, вам нужно изменить is_a_current_stash(): удалить строку if ".svn" in os.listdir(CURRENT_DIR):, поскольку в 1.7 WC есть только один subdir верхнего уровня.svn.

Вы можете сделать это легко, используя Intellij IDEA - Shelve Changes

Другой вариант - скопировать текущую кассу в новый каталог и отменить все ваши изменения. таким образом вы избавите себя от необходимости создавать временную ветвь на своем сервере - ведь копирование - это локальная операция, которую не все должны видеть и которую можно делать довольно часто.

После исправления вы можете обновить основную рабочую копию и удалить "область хранения".

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

Решения:

  1. Получение новой рабочей копии и работа в новой копии. (Самый простой и безопасный)
  2. Создайте ветку -> переключитесь на новую ветку -> blablabla (некоторые говорят, что это создаст мусор на сервере SVN)
  3. Создать патч -> восстановить рабочую копию -> исправить обратно (отлично работает, если у вас нет недобавленных или удаленных файлов)
  4. Использование (см. ниже)

Я старался 2.а также .

1.является самым простым и безопасным. Если вы хотите сэкономить время, используйте это решение. Не элегантно, я знаю.

3.это не мой выбор, потому что:

  • вы можете создать патч с недобавленными файлами и изменениями существующих файлов. Но он не удаляет эти не добавленные файлы после создания патча. Так что делать? Мне нужно создать патч (выбрать недобавленные файлы) -> вернуть рабочую копию -> вручную удалить все эти недобавленные файлы. Это вообще не работает.

4.будет самым элегантным способом и наиболее похожим на .

добавить недобавленные/неотслеживаемые файлы -> -> готово.

Видеть? По сравнению с git stash -u, с той лишь разницей, что сначала нужно добавить недобавленный файл, а затем .

Тестовая среда:

Я тестирую всех, кто использует клиент Windows Tortoise SVN с копией общего доступа к сети (SAMBA) и локальными репозиториями, созданными клиентом Windows Tortoise SVN.

Поэтому я не знаю, как все может быть иначе, если вы используете сервер SVN, который отличается от локального общего ресурса . Но я думаю shelveбудет работать в любых ситуациях, поскольку это локальная операция/функция.

Я всегда держу вторую проверку, которую я называю "trunk_clean". Всякий раз, когда мне нужно сделать быстрое изолированное изменение, связанное с тем, что я делаю, я просто вместо этого фиксирую эту проверку.

Я также хотел эту функцию. В настоящее время я использую TortoiseSVN.

Я не нашел надежного решения, кроме как экспортировать дерево, вернуться обратно в репозиторий, внести свои изменения и зафиксировать, а затем сравнить изменения из экспортированного дерева с моим исходным каталогом, используя такой инструмент, как Beyond Compare.

Или другое решение может состоять в том, чтобы перейти от HEAD к другому каталогу, внести изменения и зафиксировать. Когда вы будете готовы объединить их с вашей другой рабочей копией, сделайте обновление и объедините ваши изменения.

Использование:

svn cp --parents . ^/trash-stash/my-stash

Он создаст ветку из текущего местоположения и текущей ревизии, а затем передаст изменения рабочей копии в эту ветку, не переключаясь на нее.

использование: копировать SRC [@REV]... DST

SRC и DST могут быть либо путём рабочей копии (WC), либо URL:

WC  -> URL:  immediately commit a copy of WC to URL

Обратите внимание, что изменения в рабочей копии не будут автоматически отменены (cp это просто копирование изменений в новую ветку), и вы должны отменить их вручную.

Чтобы восстановить изменения, вы можете просто объединить изменения из вновь созданной ветви в вашу рабочую копию.

svn merge --ignore-ancestry ^/trash-stash/my-stash -c <commited revision>

--ignore-ancestry используется для того, чтобы не обновлять информацию о слиянии в рабочей копии.

Использование:

svn ls -v ^/trash-stash/

чтобы увидеть, что у вас на пути заначки. Принятые изменения также печатаются.

Если вам больше не нужен тайник, просто запустите:

svn rm ^/trash-stash/my-stash

Это решение лучше, чем использование патча, потому что если новые изменения в рабочей копии или в текущей ветке конфликтуют с изменениями в тайнике, вы можете разрешить конфликты с помощью средств SVN, тогда как patch в некоторых случаях просто не удастся или даже применить патч неправильно.

Идеи ветвления и исправления выше замечательны, но они не работают для меня. Я использую визуальный инструмент сравнения, поэтому работает git diff не производит текстовые патчи. Наша система сборки раскручивает новую среду каждый раз, когда создается ветка, поэтому создание временных веток "stash" может стать грязным.

Вместо этого я написал небольшой сценарий оболочки, который копирует файл в каталог "полка", добавляет метку времени и отменяет изменение. Он не такой надежный, как решения, описанные выше, но он также позволяет избежать некоторых подводных камней, с которыми я столкнулся.

В своей практике я использую git init создать Git-репозиторий в trunk каталог моего хранилища Subversion, а затем я добавляю *.git Присоски игнорируют закономерности.

После изменения некоторых файлов, если я хочу продолжить свою работу с основной линией Subversion, я просто использую git stash чтобы спрятать мою работу. После фиксации в хранилище Subversion я использую git stash pop восстановить мои модификации.

Основываясь на ответе Уолтера, я создал следующие псевдонимы в моем файле bashrc:

alias svn.stash='read -p "saving local changes in raq.patch. Existing stash in raq.patch will be overwritten. Continue?[y/N]" && [[ $REPLY =~ ^[yY] ]] && rm -f raq.patch && svn diff > raq.patch && svn revert -R .'
alias svn.stash.apply='patch -p0 < raq.patch; rm -f raq.patch'

Эти псевдонимы намного проще в использовании и запоминании.

Использование:

svn.stash для сохранения изменений и svn.stash.apply для применения stash.

Так как Subversion не поддерживает stash особенность отлично,
Я просто делаю это вручную.

Место Development а также Production(release) проект на отдельный путь.

source\code\MyApp         -- Development
release\MyApp(release)    -- Production(release)

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

Когда вам нужно выпустить его для производства, откройте производственный проект, обновите svn и выполните выпуски (сборка, экспорт... и т. Д.).

Я знаю, что это немного хлопотно, но выпуск прогресса происходит не часто (это не для меня, но я знаю, что некоторые проекты делают это) по сравнению с развитием прогресса, этот способ подходит мне.

Я использую SVN для конкретных проектов, так как члены команды проекта используют его, поэтому я должен следовать.
Лучшее решение - использовать git которая имеет совершенную систему контроля версий и лучше, чем svn,

Другие вопросы по тегам