开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜