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

312

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

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

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

sleske
источник
6
просто любопытно, а почему бы не использовать git-svn?
cmcginty
3
Некоторые важные новости: infoworld.com/d/application-development/… (цитируя: «Он также отмечает, что предстоящий выпуск Subversion 1.8 должен приблизить его к возможностям Git с такими функциями, как Git stash, в которых разработчик может вносить изменения локально». а затем откладывает их в сторону и выполняет автономную фиксацию, которая записывает завершенные изменения, когда разработчик находится в автономном режиме, и перемещает их в главное хранилище при повторном подключении разработчика. "
Себастьян ван ден Брук
1
Обновление (по состоянию на 2012-04-26): Стеллажи намечены на 1,9, без каких-либо ETA. Так что это может занять некоторое время ...
sleske
10
Обновление (по состоянию на 2012-11-17): Стеллажи намечены на 1.10. Может быть, это всегда запланировано на <следующий релиз +1>? ;-)
слеске
3
Обновление (по состоянию на 2015-03-23, два с половиной года спустя): Хорошая новость заключается в том, что откладывание еще запланировано на 1.10. Плохие новости: ETA: Q2 2015 (ориентировочно), выпуск 1.9.0 / 2017? (в лучшем случае умозрительный) Выпуск 1.10.0 ( subversion.apache.org/roadmap.html )
ribamar

Ответы:

69

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

  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
    

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

Бендин
источник
65
это приведет к большому количеству мусора на вашем сервере
Subversion
3
@ Knittl: Нет, не будет. И что еще более важно: это не приведет к потере изменений, как и ваше предложение. Это, а также наличие другой извлеченной копии магистрали / той же ветви, это только два надежных способа сделать это, которые я знаю. Если вам это неудобно, просто посмотрите другую копию и работайте над ней параллельно.
СБИ
2
@knittl: ветка может быть создана по незаметному пути, который находится за пределами расположения веток или тегов проекта по умолчанию. Например, команда может назначить project\temp\<creationdate-reason>или project\personal\<creationdate-reason>для этой цели.
Rwong
12
К сожалению, ветвь вообще должна создаваться на сервере. Дело не в том, что такие ветви дублируют много данных, а в том, что они создают много ненужных ссылок, без которых система, подобная git, не имеет.
thepeer
8
это не полезно с большим хранилищем. Это абсолютно не вариант в моей рабочей среде. И хотя я хотел бы, чтобы наш репозиторий был меньше и лучше организован, и, честно говоря, репозиторий git вместо svn, я ограничен пределами того, как наш код организован в нашей организации.
AdrianVeidt
337

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

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

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

Вальтер Мундт
источник
13
Это случайный дубликат stackoverflow.com/questions/1554278/… - отправьте туда голоса.
Уолтер Мундт
2
Кажется, он также не содержит двоичных файлов, что раздражает. По крайней мере при использовании TortoiseSVN для генерации патча.
angularsen
1
stackoverflow.com/questions/159853/… может помочь с этим.
Уолтер Мундт
6
Вы можете более или менее отслеживать метаданные, если используете svn patch patch_name.patchих patch -p0, потому что они находятся в файле исправления, а svn patch понимает их.
мат
Это не включает в себя изменения во внешнем.
congusbongus
182

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

svn diff > stash.patch
svn revert -R .

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

patch < stash.patch

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

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

knittl
источник
4
Я думаю, это не очень хорошо работает с удаленными / переименованными файлами.
JesperE
7
Смотрите поле под названием «Почему бы не использовать патчи вместо?» на svnbook.red-bean.com/en/1.5/… чтобы понять, почему это плохая идея.
СБИ
4
@Sbi: Я не думаю, что это является обоснованным оправданием понижения. Это не «плохой ответ». Это просто не идеальный ответ, вот и все. Я не думаю, что этот человек заслуживает наказания за его предложение. Вы бы предпочли, чтобы он не ответил вместо этого? Если да, то да, вы должны понизить. В противном случае это наказание добрых намерений.
Седат Капаноглу
5
в случае, если кто-то еще, например, я, подумал, что это выглядит как легчайшее решение, и решил попробовать его, я должен был использовать patch -p0 <stash.patch - в противном случае он жаловался на невозможность найти файлы для исправления
CupawnTae
4
Этот совет особенно полезен, если вы пришли из git-фона и вынуждены использовать SVN по разным причинам. Небольшое улучшение в совете, который уже дан начинающим пользователям патча: $ patch --strip=0 < stash.patch это гарантирует, что патч не спросит у вас имя файла при применении вашего патча.
ksinkar
43

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

$ 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

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

JesperE
источник
2
Почему это отклоняется, а за "решения" голосуют, которые даже не работают, когда вы удалили / добавили файлы или изменили какие-либо свойства? Да, это не самая простая вещь, когда вы делаете это в первый раз, но, кроме того, что вы извлекаете другую копию для параллельной работы, это единственное решение, которое работает во всех случаях.
СБИ
5
Хорошее использование синтаксиса ^ для корня репо (начиная с версии 1.6). Хорошее решение, когда в вашем репо есть ствол / теги / ветви на верхнем уровне.
до
4
Мне не очень нравится размещать все эти временные ветки на сервере. Я чувствую, что это должно быть сделано локально, вместо того, чтобы загромождать сервер (и генерировать spurios, регистрирующие электронные письма, если вы генерируете почту при регистрации). Тем не менее, вариант стоит запомнить.
Слеське
3
@sleske: да, вы делаете временный тайник на сервере, но сама ветка удаляется. Во всяком случае, я думаю, что это самый быстрый и надежный способ сделать это.
JesperE
5
@sleske: SVN не является распределенной VCS, поэтому все должно быть на сервере. Так оно и есть.
ВОО
24

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

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

Редактировать ^ 3: С 1.12.0 (выпущен 2019-04-24), копирование и переименование поддерживаются . Стеллажи в версии 1.12 несовместимы с полками, созданными в предыдущих версиях.

Редактировать ^ 4: Нет никаких изменений вокруг стеллажей с 1.13.0 и 1.14.0 . Команды все еще помечены как экспериментальные, и вам нужно определить, SVN_EXPERIMENTAL_COMMANDS=shelf3чтобы включить эту функцию. Похоже, что функция в настоящее время не используется .

Замечания по дизайну можно найти в вики разработчиков .

$ 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
snipsnipsnip
источник
9

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

Вальтер Мундт
источник
Но ссылка на stackoverflow.com/questions/1554278/… Я упоминаю в комментариях выше, предлагает практическое решение, чтобы сделать тайник только в SVN.
VonC
Справедливо; на самом деле, Google привел меня к этому решению в блоге только сейчас. Я до сих пор утверждаю, что для этого спрашивающего git-svn является естественным решением.
Уолтер Мундт
Я сомневаюсь, что решение следует за переименованием файлов, так как git нет.
NO_NAME
4

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

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

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

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

cxxl
источник
Это не для меня под окнами! :(
Антонио Петрикка
4

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

Lili
источник
Может ли этот способ справиться metadata changesи directory creates/deletes? Как именно то, что git stashделает?
wonsuc
3

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

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

knittl
источник
Примечание: это по сути то же самое, что и проверка второй рабочей копии - только без проверки :-).
слеське
6
@sleske: да, без огромного количества пропускной способности, необходимой для новой проверки
knittl
Нравится вам это или нет, но этот ответ более точно отражает поведение «git stash». Создание ветки - это круто, но больше относится к полкам TFS.
Чарльз Роберто Канато
1

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

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

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

Энтони Шоу
источник
1

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

angularsen
источник
0

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

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

Райан ДеБизи
источник
0

Основываясь на ответе Уолтера, я создал следующие псевдонимы в моем файле 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.

Рахил
источник
0

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

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

yhluo
источник
2
На самом деле это хорошее решение! Многие другие решения используют сторонние инструменты для решения проблемы; этот использует Git как сторонний инструмент. Это имеет несколько преимуществ: 1) Git очень общий и мощный. 2) У многих людей уже установлен Git.
Лий
Мне любопытно, как это работает, если вы не делаете git commit.
B2K
0

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

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в некоторых случаях просто не удастся или даже примените патч неправильно.

anton_rh
источник
-1

Поскольку Subversion не поддерживает эту stashфункцию идеально,
я просто делаю это вручную.

Поместите Developmentи Production(release)спроецируйте на отдельный путь.

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

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

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

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

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

wonsuc
источник
Не совсем понятно, что вы делаете (какая версия проверена в каталогах, о которых вы упоминаете?), Но выглядит как копия ответа с наибольшим количеством голосов («Получите новую рабочую копию»).
Слеск
@sleske Извините, я не читал детали вашего дела. В моем случае, я только нужно devи prod, 2 ситуации. Разработать совершенно новый функционал было бы сложно с SVN. Я не уверен, есть ли ясный способ решить ваше дело в мире SVN.
wonsuc