Remover automaticamente arquivos não versionados do Subversion


111

Alguém conhece uma maneira de remover recursivamente todos os arquivos em uma cópia de trabalho que não estão sob controle de versão? (Eu preciso disso para obter resultados mais confiáveis ​​no meu VMware de compilação automática.)


7
Sou um usuário SVN e tenho comparado Git a SVN para ver se quero fazer a troca. parece que este pode ser outro exemplo onde o Git brilha com seu comando "git clean".
jpierson

3
Ou hg purge --allno Mercurial.
Brendan Long

Duplicado de stackoverflow.com/questions/2803823/… onde há muito mais atividades úteis.
Heath Raftery de

Respostas:


26

Editar:

O Subversion 1.9.0 introduziu uma opção para fazer isso:

svn cleanup --remove-unversioned

Antes disso, uso este script Python para fazer isso:

import os
import re

def removeall(path):
    if not os.path.isdir(path):
        os.remove(path)
        return
    files=os.listdir(path)
    for x in files:
        fullpath=os.path.join(path, x)
        if os.path.isfile(fullpath):
            os.remove(fullpath)
        elif os.path.isdir(fullpath):
            removeall(fullpath)
    os.rmdir(path)

unversionedRex = re.compile('^ ?[\?ID] *[1-9 ]*[a-zA-Z]* +(.*)')
for l in  os.popen('svn status --no-ignore -v').readlines():
    match = unversionedRex.match(l)
    if match: removeall(match.group(1))

Parece funcionar muito bem.


1
Ainda funciona para mim com Python 2.7.2. Warren P: Você pode fornecer mais detalhes?
Thomas Watnedal

Acho que foi apenas um problema com o Python 2.6. Funciona novamente para mim em 2.7.
Warren P

1
Downvote: A outra solução abaixo svn cleanup --remove-unversionedé melhor. E é para Subversion 1.9.0 (esta versão é de 2015). É estável e normal.
tres.14159

138

isso funciona para mim no bash:

 svn status | egrep '^\?' | cut -c8- | xargs rm

Seth Reno é melhor:

svn status | grep ^\? | cut -c9- | xargs -d \\n rm -r 

Ele lida com pastas não versionadas e espaços em nomes de arquivos

Conforme os comentários abaixo, isso só funciona em arquivos que o subversion não conhece (status =?). Qualquer coisa que a subversão não saber sobre (incluindo arquivos ignorados / pastas) não será excluído.

Se você estiver usando o subversion 1.9 ou superior, você pode simplesmente usar o comando svn cleanup com as opções --remove-unversioned e --remove-ignore


6
Também pode ser usado no Windows no cygwin.
Honza

9
Você pode considerar adicionar a opção -d a xargs para nomes de arquivos com espaços e a opção -r a rm para quaisquer diretórios adicionados: svn status | grep ^ \? | cut -c9- | xargs -d \\ n rm -r
Seth Reno

4
Eu também tive problemas com a opção -d em execução no OS X, minha alternativa é a seguinte, que traduz as quebras de linha em caracteres nulos e usa a opção -0 em xargs para lidar com espaços em nomes de arquivos: svn status | grep ^ \? | cut -c9- | tr '\ n' '\ 0' | xargs -0 rm
Brian Webster,

3
Se você desaprovar comandos que dependem do número exato de caracteres na saída de outro comando:svn status | grep "^?" | awk '{print $2}' | xargs -d \\n rm -r
Michael Schlottke-Lakemper

3
@Pavel Dê uma olhada na opção xargs --no-run-if-empty
Ken

71

Eu encontrei esta página enquanto procuro fazer a mesma coisa, embora não para uma construção automatizada.

Depois de procurar um pouco mais, descobri o ' Menu de contexto estendido ' no TortoiseSVN. Segure a tecla shift e clique com o botão direito na cópia de trabalho. Existem agora opções adicionais no menu do TortoiseSVN incluindo ' Excluir itens não versionados ... '.

Embora talvez não seja aplicável a esta questão específica (ou seja, no contexto de uma construção automatizada), pensei que poderia ser útil para outras pessoas que procuram fazer a mesma coisa.


Ótimo! No XP, ele só funciona na visualização de lista (lado direito do explorer) e não na visualização em árvore (lado esquerdo).
Christopher Oezbek

Fantástico, só mandar para a lixeira, seria legal deletar direto. Exatamente o que eu precisava.
Dean Thomas

Você também pode automatizar isso na linha de comando com o TortoiseProc.exe do TortoiseSVN: detalhes na minha resposta abaixo.
stevek_mcc


9

Se você estiver na linha de comando do Windows,

for /f "tokens=2*" %i in ('svn status ^| find "?"') do del %i

Versão melhorada:

for /f "usebackq tokens=2*" %i in (`svn status ^| findstr /r "^\?"`) do svn delete --force "%i %j"

Se você usar isso em um arquivo em lote, será necessário dobrar %:

for /f "usebackq tokens=2*" %%i in (`svn status ^| findstr /r "^\?"`) do svn delete --force "%%i %%j"

1
Isso meio que funcionou para mim. No entanto, pareceu engasgar com algumas pastas não versionadas.
jpierson

7

Eu adicionei isso ao meu perfil do Windows PowerShell

function svnclean {
    svn status | foreach { if($_.StartsWith("?")) { Remove-Item $_.substring(8) -Verbose } }
}

2
@FelipeAlvarez Sim. Sim nós fazemos. Não é a melhor coisa desde o pão fatiado, mas é melhor do que lote. Eu diria que é pelo menos tão útil quanto o bash, provavelmente um pouco mais, já que você pode inserir assemblies .NET.
jpmc26

Ele sofre da tendência abominável da microsoft à verbosidade (não apenas no comprimento do nome do comando, mas na impossibilidade geral de fazer qualquer coisa sem copiar trechos gigantes da internet), mas é chocantemente útil e bastante bem pensado.
Warren P

1
Você pode adicionar --no-ignorea svn statuse -RecurseaRemove-Item
Kevin Smyth

5

Linha de comando do Linux:

svn status --no-ignore | egrep '^[?I]' | cut -c9- | xargs -d \\n rm -r

Ou, se alguns de seus arquivos pertencerem ao root:

svn status --no-ignore | egrep '^[?I]' | cut -c9- | sudo xargs -d \\n rm -r

Isso é baseado na resposta de Ken. (A resposta de Ken pula os arquivos ignorados; minha resposta os exclui).


5

Basta fazer isso no shell do Unix com:

rm -rf `svn st . | grep "^?" | cut -f2-9 -d' '`

Isso não funciona se o número de arquivos a serem excluídos exceder o número máximo de argumentos de linha de comando. Veja também as respostas baseadas em xargs.
maxschlepzig de

4

Você não pode simplesmente exportar para um novo local e construir a partir daí?


1
Para uma construção automatizada, gostaria de uma exportação limpa.
g.

1
Idealmente, você faria isso, mas isso é problemático se o seu checkout for muito grande. Esse é provavelmente o motivo pelo qual o OP pediu: tornar a construção mais curta.
jpmc26

4

Se você tem TortoiseSVN em seu caminho e está no diretório certo:

TortoiseProc.exe /command:cleanup /path:"%CD%" /delunversioned /delignored /nodlg /noui

As opções são descritas na ajuda do TortoiseSVN para /command:cleanup:

Use / noui para evitar que a caixa de diálogo de resultado apareça informando sobre a conclusão da limpeza ou mostrando uma mensagem de erro). / noprogressui também desativa a caixa de diálogo de progresso. / nodlg desativa a exibição da caixa de diálogo de limpeza onde o usuário pode escolher o que exatamente deve ser feito na limpeza. As ações disponíveis podem ser especificadas com as opções / cleanup para limpeza de status, / revert, / delunversioned, / delignored, / refreshshell e / externals.


4

Se você estiver usando o tortoise svn, existe um comando oculto para fazer isso. Segure a tecla shift enquanto clica com o botão direito em uma pasta para abrir o menu de contexto no Windows Explorer. Você receberá um comando "Excluir itens não versionados".

consulte a parte inferior desta página para obter detalhes ou a captura de tela abaixo que destaca os recursos estendidos com as estrelas verdes e aquele de interesse com o retângulo amarelo ...

SVN menu de contexto estendido vs menu padrão



3

Minha conversão em C # do script Thomas Watnedals Python:

Console.WriteLine("SVN cleaning directory {0}", directory);

Directory.SetCurrentDirectory(directory);

var psi = new ProcessStartInfo("svn.exe", "status --non-interactive");
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.WorkingDirectory = directory;

using (var process = Process.Start(psi))
{
    string line = process.StandardOutput.ReadLine();
    while (line != null)
    {
        if (line.Length > 7)
        {
            if (line[0] == '?')
            {
                string relativePath = line.Substring(7);
                Console.WriteLine(relativePath);

                string path = Path.Combine(directory, relativePath);
                if (Directory.Exists(path))
                {
                    Directory.Delete(path, true);
                }
                else if (File.Exists(path))
                {
                    File.Delete(path);
                }
            }
        }
        line = process.StandardOutput.ReadLine();
    }
}

Eu prefiro mover os arquivos não versionados, apenas no caso de você precisar deles em algum lugar mais tarde.
leppie

Em uma máquina de desenvolvimento, é claro - mas no VMware de construção, isso não faria sentido, pois ninguém se conecta a ela e cria arquivos.
Stefan Schultze,

Obrigado, usei isso como parte do meu script MSBuild no cruisecontrol para limpar meu diretório de origem antes das compilações
gregmac

Começou com base em seu código e foi além: github.com/tgmayfield/svn-clean-sharp/downloads
Tom Mayfield

3
svn st --no-ignore  | grep '^[?I]' | sed 's/^[?I]  *//' | xargs -r -d '\n' rm -r

Este é um comando shell do Unix para excluir todos os arquivos que não estão sob o controle do Subversion.

Notas:

  • o stin svn sté um alias integrado para status, ou seja, o comando é equivalente asvn status
  • --no-ignoretambém inclui arquivos não-repositórios na saída de status, caso contrário, ignora por meio de mecanismos como .cvsignoreetc. - uma vez que o objetivo é ter um ponto de partida limpo para compilações, esta opção é obrigatória
  • os grepfiltros da saída de modo que apenas os arquivos desconhecidos para o subversion são deixados - as linhas que começam com a ?lista de arquivos desconhecidos para o subversion que seriam ignorados sem a --no-ignoreopção
  • o prefixo até o nome do arquivo é removido via sed
  • o xargscomando é instruído via -rpara não executar rm, quando a lista de argumentos estaria vazia
  • a -d '\n'opção diz xargspara usar uma nova linha como delimitador de forma que o comando também funcione para nomes de arquivos com espaços
  • rm -r é usado no caso de diretórios completos (que não fazem parte do repositório) precisarem ser removidos

2

Não consegui fazer nenhum dos itens acima funcionar sem dependências adicionais que não queria adicionar ao meu sistema de compilação automatizado no win32. Portanto, reuni os seguintes comandos Ant - observe que eles requerem que o JAR Ant-contrib seja instalado (eu estava usando a versão 1.0b3, a mais recente, com Ant 1.7.0).

Observe que isso exclui todos os arquivos não versionados sem aviso prévio.

  <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
  <taskdef name="for" classname="net.sf.antcontrib.logic.ForTask" />

  <macrodef name="svnExecToProperty">
    <attribute name="params" />
    <attribute name="outputProperty" />
    <sequential>
      <echo message="Executing Subversion command:" />
      <echo message="  svn @{params}" />
      <exec executable="cmd.exe" failonerror="true"
            outputproperty="@{outputProperty}">
        <arg line="/c svn @{params}" />
      </exec>
    </sequential>
  </macrodef>

  <!-- Deletes all unversioned files without warning from the 
       basedir and all subfolders -->
  <target name="!deleteAllUnversionedFiles">
    <svnExecToProperty params="status &quot;${basedir}&quot;" 
                       outputProperty="status" />
    <echo message="Deleting any unversioned files:" />
    <for list="${status}" param="p" delimiter="&#x0a;" trim="true">
      <sequential>
        <if>
          <matches pattern="\?\s+.*" string="@{p}" />
          <then>
            <propertyregex property="f" override="true" input="@{p}" 
                           regexp="\?\s+(.*)" select="\1" />
            <delete file="${f}" failonerror="true" />
          </then>
        </if>
      </sequential>
    </for>
    <echo message="Done." />
  </target>

Para uma pasta diferente, altere a ${basedir}referência.


1
Nota: exclui apenas arquivos não versionados; não remove pastas vazias não versionadas.

2
svn status --no-ignore | awk '/^[I\?]/ {system("echo rm -r " $2)}'

remova o eco se tiver certeza do que deseja fazer.


1
Isso é inferior às respostas baseadas em xargs porque para n arquivos excluídos, há n /bin/she n rmprocessos bifurcados.
maxschlepzig

Acordado. Obrigado pela informação do xargs.
Aria

2

Já que todo mundo está fazendo isso ...

svn status | grep ^? | awk '{print $2}' | sed 's/^/.\//g' | xargs rm -R

1

Pode muito bem contribuir com outra opção

svn status | awk '{if($2 !~ /(config|\.ini)/ && !system("test -e \"" $2 "\"")) {print $2; system("rm -Rf \"" $2 "\"");}}'

O /(config|.ini)/ é para meus próprios propósitos.

E pode ser uma boa idéia adicionar --no-ignore ao comando svn



1

Solução pura para Windows cmd / bat:

@echo off

svn cleanup .
svn revert -R .
For /f "tokens=1,2" %%A in ('svn status --no-ignore') Do (
     If [%%A]==[?] ( Call :UniDelete %%B
     ) Else If [%%A]==[I] Call :UniDelete %%B
   )
svn update .
goto :eof

:UniDelete delete file/dir
if "%1"=="%~nx0" goto :eof
IF EXIST "%1\*" ( 
    RD /S /Q "%1"
) Else (
    If EXIST "%1" DEL /S /F /Q "%1"
)
goto :eof

Na verdade, este script não excluiu meus arquivos. Talvez devido a espaços nele. A resposta de uma linha de @SukeshNambiar funcionou.
Christiaan Westerbeek

1

Tentei a versão de Seth Reno a partir desta resposta, mas não funcionou para mim. Tive 8 caracteres antes do nome do arquivo, não 9 usados ​​em cut -c9-.

Portanto, esta é minha versão com em sedvez de cut:

svn status | grep ^\? | sed -e 's/\?\s*//g' | xargs -d \\n rm -r

1

Se você gosta de PowerShell:

svn status --no-ignore | ?{$_.SubString(0,1).Equals("?")} | foreach { remove-item -Path (join-Path .\ $_.Replace("?","").Trim()) -WhatIf }

Remova o sinalizador -WhatIf para que o comando realmente execute as exclusões. Caso contrário, ele apenas exibirá o que faria se fosse executado sem o -WhatIf.


1

Eu adicionaria isso como um comentário à resposta de Thomas Watnedal , mas ainda não posso.

Um pequeno problema com ele (que não afetará o Windows) é que ele verifica apenas arquivos ou diretórios. Para sistemas tipo Unix onde links simbólicos podem estar presentes, é necessário mudar a linha:

if os.path.isfile(fullpath):

para

if os.path.isfile(fullpath) or os.path.islink(fullpath):

para também remover links.

Para mim, mudar a última linha if match: removeall(match.group(1))para

    if match:
        print "Removing " + match.group(1)
        removeall(match.group(1))

para que ele exiba o que está removendo também foi útil.

Dependendo do caso de uso, a ?[\?ID]parte da expressão regular pode ser melhor ?[\?I], pois Dtambém remove arquivos excluídos que estavam sob controle de versão. Quero usar isso para criar uma pasta limpa e com check-in, de modo que não haja arquivos em um Destado.


1

@zhoufei Testei sua resposta e aqui está a versão atualizada:

FOR /F "tokens=1* delims= " %%G IN ('svn st %~1 ^| findstr "^?"') DO del /s /f /q "%%H"
FOR /F "tokens=1* delims= " %%G IN ('svn st %~1 ^| findstr "^?"') DO rd /s /q "%%H"
  • Você deve usar duas %marcas na frente de G e H
  • Mude a ordem: primeiro remova todos os arquivos, depois remova todos os diretórios
  • (opcional :) Em vez de %~1pode ser usado qualquer nome de diretório, usei isso como uma função em um arquivo bat, então %~1é o primeiro parâmetro de entrada

0

Se você não quiser escrever nenhum código, svn2.exe de svn2svn faz isso, também há um artigo sobre como ele é implementado. As pastas e arquivos excluídos são colocados na lixeira.

Execute "svn2.exe sync [path]".


0

Para as pessoas que gostam de fazer isso com perl em vez de python, shell Unix, java, etc. Aqui, um pequeno script perl que executa o jib também.

Nota: Isso também remove todos os diretórios não versionados

#!perl

use strict;

sub main()

{

    my @unversioned_list = `svn status`;

    foreach my $line (@unversioned_list)

    {

        chomp($line);

        #print "STAT: $line\n";

        if ($line =~/^\?\s*(.*)$/)

        {

            #print "Must remove $1\n";

            unlink($1);

            rmdir($1);

        }

    }

}

main();


0

Uma maneira limpa de fazer isso no PERL seria:

#!/usr/bin/perl
use IO::CaptureOutput 'capture_exec'

my $command = sprintf ("svn status --no-ignore | grep '^?' | sed -n 's/^\?//p'");

my ( $stdout, $stderr, $success, $exit_code ) = capture_exec ( $command );
my @listOfFiles = split ( ' ', $stdout );

foreach my $file ( @listOfFiles )
{ # foreach ()
    $command = sprintf ("rm -rf %s", $file);
    ( $stdout, $stderr, $success, $exit_code ) = capture_exec ( $command );
} # foreach ()

0

Usei cerca de 3 horas para gerar isso. Demoraria 5 minutos para fazê-lo no Unix. O problema principal era: espaços nos nomes das pastas Win, impossibilidade de editar %% i e problema com a definição de vars no loop cmd do Win.

setlocal enabledelayedexpansion

for /f "skip=1 tokens=2* delims==" %%i in ('svn status --no-ignore --xml ^| findstr /r "path"') do (
@set j=%%i
@rd /s /q !j:~0,-1!
)

0

O fragmento de código C # acima não funcionou para mim - eu tenho o cliente svn tartaruga e as linhas são formatadas de forma ligeiramente diferente. Aqui está o mesmo fragmento de código acima, apenas reescrito para funcionar e usando regex.

        /// <summary>
    /// Cleans up svn folder by removing non committed files and folders.
    /// </summary>
    void CleanSvnFolder( string folder )
    {
        Directory.SetCurrentDirectory(folder);

        var psi = new ProcessStartInfo("svn.exe", "status --non-interactive");
        psi.UseShellExecute = false;
        psi.RedirectStandardOutput = true;
        psi.WorkingDirectory = folder;
        psi.CreateNoWindow = true;

        using (var process = Process.Start(psi))
        {
            string line = process.StandardOutput.ReadLine();
            while (line != null)
            {
                var m = Regex.Match(line, "\\? +(.*)");

                if( m.Groups.Count >= 2 )
                {
                    string relativePath = m.Groups[1].ToString();

                    string path = Path.Combine(folder, relativePath);
                    if (Directory.Exists(path))
                    {
                        Directory.Delete(path, true);
                    }
                    else if (File.Exists(path))
                    {
                        File.Delete(path);
                    }
                }
                line = process.StandardOutput.ReadLine();
            }
        }
    } //CleanSvnFolder
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.