Subversion, mark a file deleted or moved after the fact
Let's say that I have two files in a Subversion repo:
workingcopy/
file1.txt
file2.txt
And then I rename one, external to SVN:
$ mv file1.txt fileA.txt
Now, SVN marks file1.txt as missing, fileA.txt
as unversioned
$ svn st
! file1.txt
? fileA.txt
As far as SVN knows, I deleted file1.txt
and created a completely different file fileA.txt
, so it won't know to track changes between the files.
EDIT: this does work, I just can't spell filenames correctly :)
Similarly, if you delete a file
$ rm file2.txt
$ svn st
! file2.txt
SVN only knows that a file went missing, and trying to mark it removed doesn't work:
$ svn remove file2.txt
svn: 'file2.txt' does not exist
I know that in Mercurial, you can mark a file as moved, copied, deleted, etc after the fact with the --a开发者_如何学Gofter
flag, regardless of what Mercurial sees in the working copy.
Is there a similar trick in SVN?
Maybe you need to update your version of Subversion:
$ svn --version
svn, version 1.6.16 (r1073529)
$ rm data.xml
$ svn status
! data.xml
$ svn rm data.xml
D data.xml
$ svn status
D data.xml
The same thing does not work with rename, but it would be super easy to write a shell script that does it:
#!/bin/sh
mv $2 $1
svn rename $1 $2
Or just for fun you could add the following to your .bashrc or .bash_profile:
svn_mv_after()
{
mv $2 $1
svn mv $1 $2
}
alias svnmva=svn_mv_after
If you are using TortiseSVN you can do this from the commit screen. Right click on the deleted and added file (the same file, just moved) together you get an option to repair the move.
http://tortoisesvn.net/repairmoves.html
No SVN has no such thing, cause you left SVN by using an operation command "mv .." instead of "svn mv ..." for "rm ..." this is the same as well...
SVN follows filenames with the meta information "contents" whereas in git, hg and bzr the contents is follwed with the meta information file name. This is the reason why you can do things in git, hg(i don't know), bzr(i don't know as well) if you can use operation system commands for renaming and deleting files.
What you can do after you recognized that you missed something...for example if you accidently deleted a file:
svn revert deleted.file.ext
svn rm deleted.file.ext
In the case of renaming a file you can do this only in the case you didn't change anything in the new file afterwards you renamed it by
mv file1.txt fileA.txt
you can do the same
svn revert file1.txt
delete the fileA.txt first and then
svn mv file1.txt fileA.txt
If you have changed something in the renamed file (fileA.txt) just make a copy of that file and do the same procecure and replace the file contents after "svn mv ..."
I run Arch Linux and have the following in my .bashrc
. It can handle a new file being in location you moved the source from and should be able to take most weird paths (e.g., starting with a dash or containing spaces). You my need to install realpath
.
svma() {
local dst src tmp
# Check arguments
if [[ $# != 2 ]]; then
echo 'Usage: svma SRC DST'
return 1
fi
# Ensure paths start with a slash, not a dash
src=$(realpath -- "$1")
dst=$(realpath -- "$2")
# If there is a new file at the path of the source, move it to a
# temporary name in the same directory
if [[ -e "$src" ]]; then
tmp=$(mktemp "--tmpdir=$(dirname "$src")" -u)
mv "$src" "$tmp"
fi
# Restore the source and then have Subversion move it
mv "$dst" "$src"
svn mv "$src" "$dst"
# If required, move the new file back to the source path
if [[ -v tmp ]]; then
mv "$tmp" "$src"
fi
}
I adjusted @Peter Sutton's solution a bit to handle the cases of the directory for SRC
being missing, the directory for DST
not being versioned yet, and taking an optional third parameter FILE
if the source and destination filenames are the same (if FILE
is specified, SRC
and DST
are directories). Again, realpath is needed, and I've only tested this in Cygwin with SVN version 1.8.11. Backup is handled using mv -b
, which will clobber any file at $src~
, and any unwanted directories created by mkdir will be need to be removed manually.
svma() {
local dst src tmp
# Check arguments
if [[ ( $# != 2 ) && ( $# != 3 ) ]]; then
echo 'Usage: svma SRC DST [FILE]'
return 1
fi
# Ensure paths start with a slash, not a dash
src=$(realpath -- "$1")
dst=$(realpath -- "$2")
if [[ $# == 2 ]]; then
srcdir=$(dirname "$src")
dstdir=$(dirname "$dst")
else
srcdir="$src"
dstdir="$dst"
src="$srcdir/$3"
dst="$dstdir/$3"
fi
mkdir -p "$srcdir"
svn add "$dstdir" --parents --depth=empty --force
# Restore the source and then have Subversion move it
mv -b "$dst" "$src"
svn mv "$src" "$dst"
# If required, move the backed-up file back to the source path
if [[ -e "$src~" ]]; then
mv "$src~" "$src"
fi
}
Here is a way of doing this without resorting to moving your file back:
/* svn_mark_moved.c
*
* gcc svn_mark_moved.c -o svn_mark_moved $(pkg-config --cflags --libs libsvn_client libsvn_subr)
*
* mv file_a file_b
* ./svn_mark_moved file_a file_b
*/
#include <stdio.h>
#include <stdlib.h>
#include <svn_client.h>
#include <svn_cmdline.h>
#include <svn_pools.h>
int main(int argc, char *argv[]) {
const char *progname = "svn_mark_moved";
if (argc < 3) {
fprintf(stderr, "%s: not enough arguments\n", progname);
return EXIT_FAILURE;
}
if (svn_cmdline_init(progname, stderr) != EXIT_SUCCESS)
return EXIT_FAILURE;
apr_pool_t *pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
svn_client_ctx_t *ctx;
svn_error_t *err = svn_client_create_context2(&ctx, NULL, pool);
if (err != NULL) {
fprintf(stderr, "%s: %s\n", progname, err->message);
return EXIT_FAILURE;
}
apr_array_header_t *src_paths = apr_array_make(pool, 0, sizeof(char *));
APR_ARRAY_PUSH(src_paths, char *) = argv[1];
const char *dst_path = argv[2];
err = svn_client_move7(src_paths, dst_path, FALSE, FALSE, FALSE, TRUE, NULL,
NULL, NULL, ctx, pool);
if (err != NULL) {
fprintf(stderr, "%s: %s\n", progname, err->message);
return EXIT_FAILURE;
}
}
The trick here is to pass TRUE to metadata_only
argument of svn_client_move7()
. I have no idea why this is not exposed by the CLI.
精彩评论