暂时搁置Subversion中未提交的更改(a la "git-stash")

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

为了不将两个不相关的更改混合在一起,在这种情况下,我想“收起”我的更改,即恢复到存储库版本,做一些其他更改,提交这些更改,然后“取回”我的更改。

git-stash允许这样做。有没有办法在Subversion中直接或通过一些插件或脚本来做到这一点?Eclipse插件也可以。

96093 次浏览

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

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

或者,另一种解决方案可能是从HEAD分支到另一个目录,进行更改并提交。一旦准备好将这些内容合并回其他工作副本,就可以进行更新并合并更改。

最简单的方法是使用一个临时分支,就像这样:

$ 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

这可以(而且可能应该)放在一个脚本中,如果更有规律的话。

当我的工作副本中的一个任务有未提交的更改时,我需要切换到另一个任务,我会做以下两件事之一:

  1. 为第二个任务签出一个新的工作副本。

    或者< / p >

  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
    

I have some scripts that help to automate this.

你可以使用svn diff将当前的更改存储到一个补丁文件中,然后恢复你的工作副本:

svn diff > stash.patch
svn revert -R .

在你实现了你的准备功能后,你可以用补丁工具应用你的补丁:

patch < stash.patch

正如其他人所注意到的,这将不适用于svn:properties和树操作(添加,删除,重命名文件和目录)。

二进制文件也可能会出现问题,我不知道补丁(或TortoiseSVN在这种情况下如何处理它们)。

另一种选择是将当前签出复制到一个新目录并恢复所有更改。这样就省去了在服务器上创建临时分支的麻烦——毕竟存储是一个本地操作,不是每个人都应该看到,而且可以经常这样做。

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

我不知道只有svn有什么简单的方法。老实说,我建议使用git-svn来创建一个充当svn工作副本的git repo,然后只使用git stash。只要将git pull替换为git svn rebase,将git push替换为git svn dcommit,你就可以保留90%的git工作流,并且仍然可以与svn服务器通信。

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

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

注意,这不会隐藏元数据更改或(我认为)目录创建/删除。(是的,svn与目录内容分开跟踪,不像git。)

在GPL 3下有一个名为svn-stash的小Python 2脚本: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中只有一个顶级的.svn子目录。

我总是保持第二次签出,我称之为“trunk_clean”。每当我需要做一个与我正在做的事情相关的快速、孤立的更改时,我就在签出时提交。

上面的分支和补丁想法很棒,但对我来说并不管用。我使用了一个可视化的差异工具,所以运行git diff不会产生基于文本的补丁。每次创建分支时,我们的构建系统都会旋转一个新环境,因此创建临时的“隐藏”分支会很混乱。

相反,我写了shell脚本,它将文件复制到“shelf”目录,添加时间戳,并恢复更改。它不如上面的解决方案健壮,但它也避免了我遇到的一些陷阱。

你可以使用Intellij IDEA - 搁置的变化轻松做到这一点

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

在修改了一些文件后,如果我想继续使用Subversion主线,我只需使用git stash来保存我的工作。在提交到Subversion存储库后,我使用git stash pop来恢复我的修改。

基于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应用保存。

截至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中创建的置物架不兼容。

在1.12.0(发布2019-04-24)中,复制和重命名是支持。1.12中的货架与早期版本创建的货架不兼容。

1.13.0(2019年10月)1.14.0(2020年5月)的货架没有变化。命令仍然被标记为实验性,您需要定义SVN_EXPERIMENTAL_COMMANDS=shelf3来启用该功能。它看起来像是目前untriaged特性。

设计说明可以在开发者的维基中找到。

$ 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 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

这个解决方案比使用patch更好,因为如果工作副本或当前分支上的新更改与stash中的更改冲突,您可以使用svn方法解决冲突,而patch在某些情况下会失败,甚至不正确地应用patch。

因为Subversion不完全支持stash特性,所以
. 0 我就像这样手动操作。< / p >

DevelopmentProduction(release)项目放置到单独的路径中。

source\code\MyApp         -- Development
release\MyApp(release)    -- Production(release)
你可以在开发路径中为你的项目工作任何新功能 你只会承诺有意义的进展,或者应该为稳定释放一些东西。< / p >

当你必须将它发布到生产环境,开放生产项目,更新svn并做一些事情来发布(构建,导出…)等等)。

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

我在特定的项目中使用svn,因为项目团队成员使用它,所以我必须遵循 最好的解决方案是使用git,它有一个完美的版本控制系统,比svn更好

我想对上面提到的所有解决方案做一个总结,因为在这个问题下很混乱。一些高投票的答案是模棱两可的,我花了很多时间来证明答案的某些部分是真的还是假的。

解决方案:

  1. 签出一个新的工作副本并在新的副本中工作。(最简单最安全的方法)
  2. 创建分支→切换到新分支→blablabla(有人说这会在SVN服务器中产生一些垃圾)
  3. 创建补丁→还原工作副本→补丁返回(如果你没有任何未添加的文件或已删除的文件,则非常有用)
  4. 使用shelve(见下文)

我试了第1、2和3条。

第一条是最简单、最安全的。如果你想节省时间,可以使用这个解决方案。我知道这并不优雅。

#3不是我的选择,因为你可以用未添加的文件和现有文件的更改创建一个补丁。但它不会在创建补丁后删除那些未添加的文件。那么该怎么办呢?我必须创建一个补丁(选择未添加的文件)→恢复工作副本→手动删除所有未添加的文件。这完全不像git stash -u那样工作。

# 4。shelve将是最优雅的方式,也是与git stash -u最相似的方式。添加未添加/未跟踪的文件→shelve→完成。看到了吗?与git stash -u相比,唯一的区别是你必须先添加未添加的文件,然后再添加shelve


测试环境:

我正在测试所有使用Windows Tortoise SVN客户端使用网络共享副本(SAMBA)和Windows Tortoise SVN客户端创建的本地回购。

因此,我不知道如果您使用的是与当地的分享不同的SVN服务器,情况会有什么不同。但我猜shelve在任何情况下都可以工作,因为它是一个本地操作。