在对存储在 Subversion 存储库中的软件进行编程时,我经常修改一些文件,然后注意到我想为我的主要工作做一些准备性更改。例如,在实现新功能时,我注意到一些可能对我有帮助的重构。
为了不混合两个不相关的更改,在这些情况下,我想“隐藏”我的更改,即恢复到存储库版本,做一些其他更改,提交这些,然后“取回”我的更改。
git-stash 允许这样做。有没有办法用 Subversion 直接或使用一些插件或脚本来做到这一点。 Eclipse 插件也可以。
This blog post 建议使用差异和补丁。
git stash 大约变成 svn diff > patch_name.patch; svn 恢复 -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 处理它们)。
$ patch --strip=0 < stash.patch
这将确保补丁在您应用补丁时不会询问您的文件名。
当我的工作副本中的一项任务有未提交的更改并且我需要切换到另一项任务时,我会做以下两件事之一:
检查第二个任务的新工作副本。或启动一个分支: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
我有一些脚本可以帮助实现这一点。
project\temp\<creationdate-reason>
或 project\personal\<creationdate-reason>
。
最简单的方法是使用临时分支,如下所示:
$ 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.10.0 (2018-04-13) 开始,您有实验性 svn shelve
command。 (TortoiseSVN supports the command) 它只是保存补丁并重新应用的助手,因此它具有与svn diff
+ patch
相同的限制(即无法处理二进制文件和重命名)。 (编辑:Looks like binary support is coming at next version 1.11.0)
编辑^2: 对于 1.11.0(2018 年 10 月 30 日发布),二进制文件是 supported。搁置重命名的文件仍然不受支持。 1.11 中的搁架与 1.10 创建的搁架不兼容。
编辑^3: 对于 1.12.0(2019 年 4 月 24 日发布),复制和重命名是 supported。 1.12 中的搁架与早期版本创建的搁架不兼容。
编辑^4:使用 1.13.0 (Oct 2019) 和 1.14.0 (May 2020) 搁置没有变化。命令仍被标记为实验性的,您需要定义 SVN_EXPERIMENTAL_COMMANDS=shelf3
以启用该功能。看起来该功能是 currently untriaged。
设计说明可在开发人员的 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
shelve
的工作原理而不是将所有命令行文档放在这里,那就更好了。 shelve
是我认为的最佳解决方案。我想写一个总结答案,比较上面提到的所有解决方案。这些答案或多或少是不完整的。
我不知道仅使用 svn 的简单方法。老实说,我建议使用 git-svn
制作一个充当 svn 工作副本的 git repo,然后只使用 git stash
。只需将 git pull
替换为 git svn rebase
并将 git push
替换为 git svn dcommit
,您实际上可以保留 90% 的 git 工作流,并且仍然与 svn 服务器通信。
在 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 子目录。
您可以使用 Intellij IDEA 轻松完成 - Shelve Changes
metadata changes
和directory creates/deletes
吗?就像 git stash
所做的那样?
另一种选择是将您当前的结帐复制到一个新目录并恢复您的所有更改。这样你就省去了在你的服务器上创建一个临时分支的麻烦——毕竟存储是一个本地操作,不是每个人都应该看到并且可以经常完成。
提交修补程序后,您可以更新主工作副本并删除“存储区”
我总是保留第二次结帐,我称之为“trunk_clean”。每当我需要做一个与我正在做的事情相关的快速、独立的更改时,我只需提交该结帐即可。
我也想要这个功能。我目前使用 TortoiseSVN。
除了导出树,恢复到存储库进行更改并提交,然后使用 Beyond Compare 之类的工具将导出树中的更改返回到我的源代码控制目录之外,我还没有找到一个快速的解决方案。
或者,另一种解决方案可能是从 HEAD 分支到另一个目录,进行更改并提交。一旦您准备好将它们合并回您的其他工作副本,请进行更新并合并您的更改。
上面的分支和修补想法很棒,但它们对我来说效果不佳。我使用视觉差异工具,因此运行 git diff
不会产生基于文本的补丁。每次创建分支时,我们的构建系统都会启动一个新环境,因此创建临时“存储”分支会变得混乱。
相反,我编写了一个 little shell script,它将文件复制到“架子”目录,添加时间戳,然后恢复更改。它不像上面的解决方案那么健壮,但它也避免了我遇到的一些陷阱。
根据沃尔特的回答,我在 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 应用存储。
在我的实践中,我使用 git init
在我的 Subversion 存储库的 trunk
目录中创建一个 Git 存储库,然后将 *.git
添加到 Suctions 忽略模式。
在修改了一些文件之后,如果我想继续使用 Subversion 主线工作,我只需使用 git stash
来存储我的工作。提交到 Subversion 存储库后,我使用 git stash pop
恢复我的修改。
利用:
svn cp --parents . ^/trash-stash/my-stash
它将从当前位置和当前修订创建一个分支,然后它将工作副本中的更改提交到该分支而不切换到它。
用法:复制 SRC[@REV]... DST SRC 和 DST 都可以是工作副本 (WC) 路径或 URL:WC -> URL:立即将 WC 的副本提交到 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
在某些情况下会失败甚至应用补丁不正确。
我想对上面提到的所有解决方案做一个总结,因为在这个问题下真是一团糟。一些高票的答案是模棱两可的,我花了很多时间来证明答案的某些部分是否正确。
解决方案:
签出新的工作副本并在新副本中工作。 (最简单和最安全的)创建一个分支 -> 切换到新分支 -> blablabla (有人说它会在 SVN 服务器中产生一些垃圾)创建一个补丁 -> 恢复工作副本 -> 补丁回来(如果你不这样做,效果很好'没有任何未添加的文件或已删除的文件)使用搁置(见下文)
我试过 1.
2.
和 3.
。
1.
是最简单和最安全的一种。如果您想节省时间,请使用此解决方案。我知道不优雅。
3.
不是我的选择,因为:
您可以使用未添加的文件和现有文件的更改创建补丁。但它不会在创建补丁后删除那些未添加的文件。那么该怎么办?我必须创建一个补丁(选择未添加的文件)-> 恢复工作副本-> 手动删除所有这些未添加的文件。这根本不像 git stash -u 那样工作。
4.
shelve
将是最优雅的方式,并且与 git stash -u
最相似。
添加未添加/未跟踪的文件-> shelve
->完毕。
看?与 git stash -u
相比,唯一的区别是您必须先添加未添加的文件,然后是 shelve
。
测试环境:
我正在测试所有使用 Windows Tortoise SVN 客户端和网络共享副本 (SAMBA) 和由 Windows Tortoise SVN 客户端创建的本地存储库的用户。
所以我不知道如果您使用的是不同于 local share 的 SVN 服务器,情况会有什么不同。但我猜 shelve
可以在任何情况下工作,因为它是 本地 操作/功能。
由于 Subversion 不完全支持 stash
功能,
我只是像这样手动操作。
将 Development
和 Production(release)
项目放置在单独的路径中。
source\code\MyApp -- Development
release\MyApp(release) -- Production(release)
您可以在开发路径中为您的项目使用任何新功能,并且您只会提交有意义的进展或者应该为稳定版本发布一些东西。
当你必须为生产发布它时,打开生产项目,更新 svn 并做一些事情来发布(构建,导出......等)。
我知道这有点麻烦,但是发布进度并不经常发生(它不适合我,但我知道有些项目会)与开发进度相比,这种方式适合我。
由于项目组成员使用svn,所以我将svn用于特定项目,所以我必须遵循。
最好的解决方案是使用具有完美版本控制系统并且优于svn
的git
。
dev
和 prod
,两种情况。使用 svn 开发全新的功能会很复杂。我不确定在 svn 世界中是否有明确的方法可以解决您的问题。
svn patch patch_name.patch
而不是patch -p0
,您可以或多或少地跟踪元数据,因为它们在补丁文件中,并且 svn patch 可以理解它们。