暂时保存Subversion中未提交的更改(la“ git-stash”)


312

在对存储在Subversion版本库中的软件进行编程时,我经常修改一些文件,然后注意我想为我的主要工作做一些准备性的更改。例如,在实现新功能时,我注意到一些重构可能会对我有所帮助。

为了不混淆两个无关的更改,在这种情况下,我想“收藏”我的更改,即还原到存储库版本,进行其他更改,提交这些更改,然后“取回”我的更改。

git-stash可以做到这一点。是否可以通过Subversion直接或使用某些插件或脚本来执行此操作。Eclipse插件也可以。


6
只是好奇,但为什么不使用git-svn?
cmcginty 2010年

3
一些相关新闻:infoworld.com/d/application-development/…(引用:“他还指出,即将发布的Subversion 1.8版本应该使其更接近Git的功能,并具有Git stash等功能,开发人员可以在其中进行本地更改。然后将它们放在一边,然后进行脱机提交,脱机提交会记录开发人员处于脱机状态时已完成的更改,并在开发人员重新连接时将其移至主存储库。”
Sebastiaan van den Broek

1
更新(截至2012-04-26):货架计划为1.9,无任何预计到达时间。所以可能要花点时间...
sleske

10
更新(截至2012-11-17):搁置时间现在定为1.10。也许总是安排在<下一发行版+1>上进行?;-)
sleske 2012年

3
更新(截至2015年3月23日,两年半后):好消息是,搁架仍计划在1.10投放。ETA是个坏消息:2015年第二季度(暂定)1.9.0版/ 2017年?(投机充其量)版本1.10.0(subversion.apache.org/roadmap.html
Ribamar酒店

Answers:


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服务器上造成大量垃圾
knittl

3
@knittl:不,不会。更重要的是:它不会像您的建议那样导致更改丢失。这是我知道的仅有的两种可靠方法,并且还有另一份已检出的trunk / same分支的副本。如果您对此感到不舒服,只需签出另一份副本并并行处理即可。
sbi

2
@knittl:可以在项目的默认分支或标签位置之外的不显眼的路径中创建分支。例如,团队可以指定project\temp\<creationdate-reason>project\personal\<creationdate-reason>为此目的。
rwong

12
仍然不幸的是,必须在服务器上完全创建分支。并不是这些分支重复了很多数据,而是它们创建了很多不必要的引用,而git这样的系统却没有这样做。
同行2012年

8
这对于大型存储库没有用。在我的工作环境中,这绝对不是一个选择。尽管我希望我们的存储库更小,组织得更好,并且坦率地说,是一个git存储库而不是svn,但我仅限于组织中代码组织方式的限制。
AdrianVeidt 2014年

337

这篇博客文章建议使用diff和patch。

  • git stash 大约变成 svn diff > patch_name.patch; svn revert -R .
  • git stash apply 变成 patch -p0 < patch_name.patch

请注意,这不会存储元数据更改或(我认为)目录创建/删除。(是的,与git不同,svn与目录内容分开地跟踪它们。)


13
这是stackoverflow.com/questions/1554278/…的意外副本–在此发送upvotes。
Walter Mundt

2
它似乎也不包含二进制文件,这很烦人。至少在使用TortoiseSVN生成补丁时。
angularsen


6
如果您使用svn patch patch_name.patch而不是,则可以或多或少地跟踪元数据patch -p0,因为它们位于修补程序文件中,并且svn patch可以理解它们。

这不包括对外部的更改。
congusbongus 2014年

182

您可以将当前的更改存储svn diff到补丁文件中,然后还原工作副本:

svn diff > stash.patch
svn revert -R .

实施准备功能后,可以使用补丁实用程序应用补丁:

patch < stash.patch

正如其他人指出的那样,这不适用于svn:properties和树操作(添加,删除,重命名文件和目录)。

二进制文件也可能带来问题,我不知道修补程序(或在这种情况下为TortoiseSVN)如何处理它们。


4
我认为,这对于删除/重命名的文件可能效果不佳。
JesperE

7
请参阅标题为“为什么不使用补丁代替”的框。请访问svnbook.red-bean.com/en/1.5/…了解为什么这是一个坏主意。
sbi

4
@sbi:我认为那不是反对投票的正当理由。这不是一个“错误的答案”。并不是所有的完美答案。我认为这个人不应该因他的建议而受到惩罚。您希望他不回答吗?如果是,则是,您应该投反对票。否则,这是在惩罚良好的意愿。
Sedat Kapanoglu,2009年

5
如果像我这样的其他人认为这看起来是最轻便的解决方案并决定尝试,则必须使用patch -p0 <stash.patch-否则它抱怨无法找到要修补的文件
CupawnTae

4
如果您来自git背景并且由于各种原因而被迫使用SVN,则此建议特别有用。已为首次使用补丁的用户提供的建议中有一个小改进: $ patch --strip=0 < stash.patch 这将确保在应用补丁时,补丁不会询问您文件名。
ksinkar 2014年

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

如果更定期地将其放在脚本中,可能应该放进脚本中。


2
为什么当您删除/添加文件或更改任何属性时,“解决方案”被否决,而“解决方案”却被否决,这为何被否决?是的,这并不是第一次进行时最容易的事情,但是,除了要签出另一个副本以并行工作之外,这是在所有情况下都可行的唯一解决方案。
sbi

5
很好地使用^语法作为回购根目录(自svn 1.6起)。当您的存储库在顶层具有主干/标签/分支时,这是一个很好的解决方案。

4
我真的不喜欢将所有这些临时分支放在服务器上。我觉得这应该在本地完成,而不是使服务器混乱(如果在签入时生成邮件,则会生成spurios签入电子邮件)。尽管如此,一个选项值得记住。
sleske

3
@sleske:是的,您正在将临时存储提交到服务器,但是分支本身已删除。无论如何,我认为这是最快,最可靠的方法。
JesperE,2009年

5
@sleske:SVN不是分布式VCS,因此所有内容都必须在服务器上。就是那样子。
sbi

24

从1.10.0(2018-04-13)起,您有了实验性svn shelve命令。(TortoiseSVN支持该命令。)它只是保存补丁并应用回来的帮手,因此它与svn diff+ 具有相同的限制patch(即无法处理二进制文件和重命名)。(编辑似乎二进制支持将在下一版本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.01.14.0周围没有变化。命令仍被标记为实验性命令,您需要定义SVN_EXPERIMENTAL_COMMANDS=shelf3以启用该功能。似乎该功能当前未尝试过

设计说明可以在开发人员的Wiki上找到。

$ 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

9

我不知道仅用svn就能做到的简单方法。老实说,我建议使用git-svn制作一个git repo来作为svn工作副本,并仅使用git stash它。只需替换为git pullwith git svn rebasegit pushgit svn dcommit您实际上就可以保留90%的git工作流程,并且仍在与svn服务器通信。


但是链接stackoverflow.com/questions/1554278/…我在上面的评论中提到确实提出了仅在svn中进行隐藏的实用解决方案。
VonC

很公平; 实际上,Google刚刚在博客上将我引向了该解决方案。对于该提问者,我仍然认为git-svn是一个自然的解决方案。
沃尔特·蒙德

我怀疑解决方案是否遵循文件重命名,因为git没有。
NO_NAME 2015年

4

svn-stash在GPL 3下有一个称为Python 2的小脚本:https : //github.com/frankcortes/svn-stash

它的工作方式与上述svn diff/patch解决方案一样,并提供将更改作为差异推送到某些本地目录中的功能。不幸的是,无法隐藏存储区,只能弹出最后一个存储区(是的,是一个堆栈,但是没有这样限制的真正原因。)但是,您始终可以将丢失的功能构建到存储区中资源。

它是为* ix编写的,但是在用os.sepWindows 替换每个“ /”之后,它也能很好地工作。

如果使用svn 1.7或更高版本,则需要更改is_a_current_stash():删除该行if ".svn" in os.listdir(CURRENT_DIR):,因为在1.7 WC中只有一个顶级.svn子目录。


在Windows下不适合我!:(
Antonio Petricca,

4

您可以使用Intellij IDEA轻松完成- 搁置更改


请问这种方式可以处理metadata changesdirectory creates/deletes?到底是什么git stash呢?
wonsuc

3

另一个选择是将您当前的结帐复制到新目录并还原所有更改。这样,您将省去在服务器上创建临时分支的麻烦-毕竟所有存储都是本地操作,不是每个人都应该看到的,并且可以经常执行。

提交修补程序后,您可以更新主工作副本并删除“存储区”


注意:这基本上与签出第二份工作副本相同-仅不签出:-)。
sleske

6
@sleske:是的,没有新签出所需要的大量带宽
knittl 2011年

不管喜欢与否,这是更贴近“ git stash”行为的答案。创建分支机构很酷,但与TFS搁置关系更大。
Charles Roberto Canato

1

我也想要这个功能。我目前使用TortoiseSVN。

除了导出树,还原到存储库进行我的更改并提交,然后使用Beyond Compare之类的工具将导出树中的更改返回到我的源代码控制目录中之外,我没有找到一种快速解决方案。

或者,另一个解决方案可能是从HEAD分支到另一个目录,进行更改并提交。一旦准备好将它们合并回其他工作副本,请进行更新并合并您的更改。


1

我总是保持第二次结帐,我称之为“ trunk_clean”。每当我需要进行与我的工作相关的快速,独立的更改时,我只会提交该结帐。


0

上面的分支和修补想法很棒,但对我来说效果不佳。我使用视觉差异工具,因此运行git diff不会产生基于文本的补丁。每次创建分支时,我们的构建系统都会启动一个新环境,因此创建临时“隐藏”分支会变得混乱。

相反,我编写了一个小shell脚本,该脚本将文件复制到“ shelf”目录中,添加时间戳,并还原更改。它不像上面的解决方案那样健壮,但是它也避免了我遇到的一些陷阱。


0

根据Walter的回答,我在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可以应用存储。


0

在我的实践中,我通常在Subversion存储git init库的trunk目录中创建一个Git存储库,然后将其添加*.git到Suctions忽略模式中。

修改了一些文件后,如果我想继续使用Subversion主线工作,我只是git stash用来存放我的工作。提交到Subversion存储库后,我用于git stash pop还原修改。


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只是CoPying对新分支的更改),您必须手动恢复它们。

要恢复更改,您只需将更改从新创建的分支合并到您的工作副本即可。

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在某些情况下甚至会失败甚至不正确地应用修补程序。


-1

由于Subversion并不stash完美地支持功能,因此
我只是这样做。

放置DevelopmentProduction(release)投影到单独的路径。

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

您可以在开发路径中为项目使用任何新功能,
并且您只会提交有意义的进度,或者应该发布一些稳定的东西。

当您必须将其发布以进行生产时,请打开生产项目,更新svn并进行发布(构建,导出等)工作。

我知道这有点麻烦,但是发布进度并不经常发生(这对我来说不行,但我知道有些项目确实如此)与开发进度相比,这种方式很适合我。

由于项目团队成员使用svn,因此我将其用于特定项目,因此我必须遵循。
最好的解决方案是使用git具有完善的版本控制系统并且优于的版本svn


尚不清楚您在做什么(您提到的目录中检出了哪个版本?),但看起来像是投票最多的答案的副本(“检出新的工作副本”)。
sleske

@sleske抱歉,我没有阅读您的案件详细信息。就我而言,我只需要devprod,2种情况。要开发全新的功能,使用svn将会很复杂。我不确定在svn世界中是否有解决您案件的明确方法。
wonsuc
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.