diff --git a/conans/client/tools/scm.py b/conans/client/tools/scm.py
index 1b8580280..c03877438 100644
--- a/conans/client/tools/scm.py
+++ b/conans/client/tools/scm.py
@@ -1,6 +1,5 @@
 
 import os
-import sys
 import re
 
 import subprocess
@@ -273,16 +272,27 @@ class SVN(SCMBase):
     def is_pristine(self):
         # Check if working copy is pristine/consistent
         if self.version >= SVN.API_CHANGE_VERSION:
-            output = self.run("status -u -r {}".format(self.get_revision()))
-            offending_columns = [0, 1, 2, 3, 4, 6, 7, 8]  # 5th column informs if the file is locked (7th is always blank)
-
-            for item in output.splitlines()[:-1]:
-                if item[0] == '?':  # Untracked file
-                    continue
-                if any(item[i] != ' ' for i in offending_columns):
-                    return False
-
-            return True
+            try:
+                output = self.run("status -u -r {} --xml".format(self.get_revision()))
+            except subprocess.CalledProcessError:
+                return False
+            else:
+                root = ET.fromstring(output)
+
+                pristine_item_list = ['external', 'ignored', 'none', 'normal']
+                pristine_props_list = ['normal', 'none']
+                for item in root.findall('.//wc-status'):
+                    if item.get('item', 'none') not in pristine_item_list:
+                        return False
+                    if item.get('props', 'none') not in pristine_props_list:
+                        return False
+
+                for item in root.findall('.//repos-status'):
+                    if item.get('item', 'none') not in pristine_item_list:
+                        return False
+                    if item.get('props', 'none') not in pristine_props_list:
+                        return False
+                return True
         else:
             import warnings
             warnings.warn("SVN::is_pristine for SVN v{} (less than {}) is not implemented, it is"

[scm] Rework SVN is_pristine() function
To help us debug your issue please explain:

- [x] I've read the [CONTRIBUTING guide](https://raw.githubusercontent.com/conan-io/conan/develop/.github/CONTRIBUTING.md).

The actual implementation of SVN `is_pristine` function could fail by the following two cases:

* When using externals there will be X in the first column and also the output will come for the main project and all externals libs, so that output.splitlines()[:-1] will not work.
* When using the -u parameter, there will be shown files that are updated on the server with an * . Because we want also to be able to checkout an specific revision, I think this column must also not be used.

May be we should use the --xml option, to better parse the files.

To get the SVN #3192 pull request into the develop branch, we postponed this issue.
diff --git a/conans/test/util/tools_test.py b/conans/test/util/tools_test.py
index ce3d0cba8..1f42725bd 100644
--- a/conans/test/util/tools_test.py
+++ b/conans/test/util/tools_test.py
@@ -5,13 +5,15 @@ import os
 import platform
 import unittest
 import uuid
+import shutil
+import subprocess
 
 from collections import namedtuple
 
 import six
 from mock.mock import patch, mock_open
 from six import StringIO
-from six.moves.urllib.parse import unquote
+from six.moves.urllib.parse import quote
 
 from conans.client.client_cache import CONAN_CONF
 from conans.model.version import Version
@@ -20,7 +22,7 @@ from conans import tools
 from conans.client.conan_api import ConanAPIV1
 from conans.client.conf import default_settings_yml, default_client_conf
 from conans.client.output import ConanOutput
-from conans.client.tools.win import vcvars_dict, vswhere
+from conans.client.tools.win import vcvars_dict, vswhere, get_cased_path
 from conans.client.tools.scm import Git, SVN
 
 from conans.errors import ConanException, NotFoundException
@@ -31,7 +33,7 @@ from conans.test.build_helpers.cmake_test import ConanFileMock
 from conans.test.utils.runner import TestRunner
 from conans.test.utils.test_files import temp_folder
 from conans.test.utils.tools import TestClient, TestBufferConanOutput, create_local_git_repo, \
-    SVNLocalRepoTestCase, StoppableThreadBottle
+    SVNLocalRepoTestCase, StoppableThreadBottle, try_remove_readonly
 
 from conans.tools import which
 from conans.tools import OSInfo, SystemPackageTool, replace_in_file, AptTool, ChocolateyTool,\
@@ -1624,9 +1626,8 @@ class SVNToolTestsBasic(SVNLocalRepoTestCase):
 
         self.assertFalse(svn.is_pristine())
         svn.checkout(url=project_url)  # SVN::clone over a dirty repo reverts all changes (but it doesn't delete non versioned files)
-        if svn.version >= SVN.API_CHANGE_VERSION:  # SVN::is_pristine returns always False.
-            self.assertTrue(svn.is_pristine())
-            # self.assertFalse(os.path.exists(new_file))
+        self.assertEqual(open(mod_file).read(), "contents")
+        self.assertFalse(svn.is_pristine())
 
     def test_excluded_files(self):
         project_url, _ = self.create_project(files={'myfile': "contents"})
@@ -1805,9 +1806,10 @@ class SVNToolTestsPristine(SVNLocalRepoTestCase):
         tmp_folder = self.gimme_tmp()
         svn = SVN(folder=tmp_folder)
         svn.checkout(url=self.repo_url)
+        self.assertTrue(svn.is_pristine())
         with open(os.path.join(tmp_folder, "not_tracked.txt"), "w") as f:
             f.write("content")
-        self.assertTrue(svn.is_pristine())
+        self.assertFalse(svn.is_pristine())
 
     def test_ignored_file(self):
         tmp_folder = self.gimme_tmp()
@@ -1848,6 +1850,146 @@ class SVNToolTestsPristine(SVNLocalRepoTestCase):
         svn2.run('revert . -R')
         self.assertTrue(svn2.is_pristine())
 
+    def test_mixed_revisions(self):
+        project_url, _ = self.create_project(files={'myfile': "cc", 'another': 'aa'})
+        svn = SVN(folder=self.gimme_tmp())
+        svn.checkout(url=project_url)
+        with open(os.path.join(svn.folder, 'myfile'), "a") as f:
+            f.write('more')
+        svn.run('commit -m "up version"')
+        self.assertFalse(svn.is_pristine())
+
+    def test_missing_remote(self):
+        repo_url = self.gimme_tmp()
+        subprocess.check_output('svnadmin create "{}"'.format(repo_url), shell=True)
+        project_url = SVN.file_protocol + quote(repo_url.replace("\\", "/"), safe='/:')
+
+        svn = SVN(folder=self.gimme_tmp())
+        svn.checkout(url=project_url)
+        self.assertTrue(svn.is_pristine())
+
+        shutil.rmtree(repo_url, ignore_errors=False, onerror=try_remove_readonly)
+        self.assertFalse(os.path.exists(repo_url))
+        self.assertFalse(svn.is_pristine())
+
+
+class SVNToolTestsPristineWithExternalFile(SVNLocalRepoTestCase):
+
+    def _propset_cmd(self, relpath, rev, url):
+        return 'propset svn:externals "{} -r{} {}" .'.format(relpath, rev, url)
+
+    def setUp(self):
+        project_url, _ = self.create_project(files={'myfile': "contents"})
+        project2_url, rev = self.create_project(files={'nestedfile': "contents"})
+
+        self.svn = SVN(folder=self.gimme_tmp())
+        self.svn.checkout(url=project_url)
+        self.svn.run(self._propset_cmd("subrepo_nestedfile", rev, project2_url + '/nestedfile'))
+        self.svn.run('commit -m "add external"')
+        self.svn.update()
+        self.assertTrue(self.svn.is_pristine())
+
+    def test_modified_external(self):
+        with open(os.path.join(self.svn.folder, "subrepo_nestedfile"), "a") as f:
+            f.write("cosass")
+        self.assertFalse(self.svn.is_pristine())
+
+
+class SVNToolTestsPristineWithExternalsNotFixed(SVNLocalRepoTestCase):
+
+    def _propset_cmd(self, relpath, url):
+        return 'propset svn:externals "{} {}" .'.format(relpath, url)
+
+    def setUp(self):
+        project_url, _ = self.create_project(files={'myfile': "contents"})
+        project2_url, _ = self.create_project(files={'nestedfile': "contents"})
+
+        self.svn = SVN(folder=self.gimme_tmp())
+        self.svn.checkout(url=project_url)
+        self.svn.run(self._propset_cmd("subrepo", project2_url))
+        self.svn.run('commit -m "add external"')
+        self.svn.update()
+        self.assertTrue(self.svn.is_pristine())
+
+        self.svn2 = SVN(folder=self.gimme_tmp())
+        self.svn2.checkout(url=project2_url)
+        self.assertTrue(self.svn.is_pristine())
+
+    def test_modified_external(self):
+        with open(os.path.join(self.svn2.folder, "nestedfile"), "a") as f:
+            f.write("cosass")
+        self.svn2.run('commit -m "another"')
+        self.svn2.update()
+        self.assertTrue(self.svn2.is_pristine())
+
+        # Known: without fixed external, it won't be pristine if there is something new in remote.
+        self.assertFalse(self.svn.is_pristine())
+
+
+class SVNToolTestsPristineWithExternalsFixed(SVNLocalRepoTestCase):
+
+    def _propset_cmd(self, relpath, rev, url):
+        return 'propset svn:externals "{} -r{} {}" .'.format(relpath, rev, url)
+
+    def setUp(self):
+        project_url, _ = self.create_project(files={'myfile': "contents"})
+        project2_url, rev = self.create_project(files={'nestedfile': "contents"})
+
+        self.svn = SVN(folder=self.gimme_tmp())
+        self.svn.checkout(url=project_url)
+        self.svn.run(self._propset_cmd("subrepo", rev, project2_url))
+        self.svn.run('commit -m "add external"')
+        self.svn.update()
+        self.assertTrue(self.svn.is_pristine())
+
+        self.svn_subrepo = SVN(folder=os.path.join(self.svn.folder, 'subrepo'))
+        self.assertTrue(self.svn_subrepo.is_pristine())
+
+    def test_modified_external(self):
+        with open(os.path.join(self.svn.folder, "subrepo", "nestedfile"), "a") as f:
+            f.write("cosass")
+        self.assertFalse(self.svn_subrepo.is_pristine())
+        self.assertFalse(self.svn.is_pristine())
+
+    def test_commit_external(self):
+        with open(os.path.join(self.svn.folder, "subrepo", "nestedfile"), "a") as f:
+            f.write("cosass")
+        self.svn_subrepo.run('commit -m "up external"')
+        self.assertFalse(self.svn_subrepo.is_pristine())
+        self.assertFalse(self.svn.is_pristine())
+
+        self.svn_subrepo.update()
+        self.assertTrue(self.svn_subrepo.is_pristine())
+        self.assertFalse(self.svn.is_pristine())
+
+    def test_untracked_external(self):
+        with open(os.path.join(self.svn.folder, "subrepo", "other_file"), "w") as f:
+            f.write("cosass")
+        self.assertFalse(self.svn_subrepo.is_pristine())
+        self.assertFalse(self.svn.is_pristine())
+
+    def test_ignored_external(self):
+        file_to_ignore = "secret.txt"
+        with open(os.path.join(self.svn_subrepo.folder, file_to_ignore), "w") as f:
+            f.write("cosas")
+
+        self.svn_subrepo.run("propset svn:ignore {} .".format(file_to_ignore))
+        self.assertFalse(self.svn_subrepo.is_pristine())
+        self.assertFalse(self.svn.is_pristine())
+
+        self.svn_subrepo.run('commit -m "add ignored file"')
+        self.assertTrue(self.svn_subrepo.is_pristine())
+        self.assertFalse(self.svn.is_pristine())
+
+        subrepo_rev = self.svn_subrepo.get_revision()
+        self.svn.run(self._propset_cmd("subrepo", subrepo_rev, self.svn_subrepo.get_remote_url()))
+        self.assertTrue(self.svn_subrepo.is_pristine())
+        self.assertFalse(self.svn.is_pristine())
+
+        self.svn.run('commit -m "change property"')
+        self.svn.update()
+        self.assertTrue(self.svn.is_pristine())
+
 
 class SVNToolsTestsRecipe(SVNLocalRepoTestCase):
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
nose nose-cov pytest
apt-get update apt-get install -y gcc
3.6
conans/requirements.txt conans/requirements_server.txt conans/requirements_dev.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astroid==2.11.7
attrs==22.2.0
beautifulsoup4==4.12.3
bottle==0.12.25
certifi==2021.5.30
charset-normalizer==2.0.12
codecov==2.1.13
colorama==0.3.9
-e git+https://github.com/conan-io/conan.git@487ff560f48aa2103b4e6543cbc51cac698b2190#egg=conan
cov-core==1.15.0
coverage==4.2
deprecation==2.0.7
dill==0.3.4
distro==1.1.0
fasteners==0.19
future==0.16.0
idna==3.10
importlib-metadata==4.8.3
iniconfig==1.1.1
isort==5.10.1
lazy-object-proxy==1.7.1
mccabe==0.7.0
mock==1.3.0
node-semver==0.2.0
nose==1.3.7
nose-cov==1.6
packaging==21.3
parameterized==0.8.1
patch==1.16
pbr==6.1.1
platformdirs==2.4.0
pluggy==1.0.0
pluginbase==0.7
py==1.11.0
Pygments==2.14.0
PyJWT==1.7.1
pylint==2.13.9
pyparsing==3.1.4
pytest==7.0.1
PyYAML==3.13
requests==2.27.1
six==1.17.0
soupsieve==2.3.2.post1
tomli==1.2.3
tqdm-conan==4.27.0
typed-ast==1.5.5
typing_extensions==4.1.1
urllib3==1.26.20
waitress==2.0.0
WebOb==1.8.9
WebTest==2.0.35
wrapt==1.16.0
zipp==3.6.0

name: conan
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - astroid==2.11.7
      - attrs==22.2.0
      - beautifulsoup4==4.12.3
      - bottle==0.12.25
      - charset-normalizer==2.0.12
      - codecov==2.1.13
      - colorama==0.3.9
      - cov-core==1.15.0
      - coverage==4.2
      - deprecation==2.0.7
      - dill==0.3.4
      - distro==1.1.0
      - fasteners==0.19
      - future==0.16.0
      - idna==3.10
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - isort==5.10.1
      - lazy-object-proxy==1.7.1
      - mccabe==0.7.0
      - mock==1.3.0
      - node-semver==0.2.0
      - nose==1.3.7
      - nose-cov==1.6
      - packaging==21.3
      - parameterized==0.8.1
      - patch==1.16
      - pbr==6.1.1
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - pluginbase==0.7
      - py==1.11.0
      - pygments==2.14.0
      - pyjwt==1.7.1
      - pylint==2.13.9
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pyyaml==3.13
      - requests==2.27.1
      - six==1.17.0
      - soupsieve==2.3.2.post1
      - tomli==1.2.3
      - tqdm-conan==4.27.0
      - typed-ast==1.5.5
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - waitress==2.0.0
      - webob==1.8.9
      - webtest==2.0.35
      - wrapt==1.16.0
      - zipp==3.6.0
prefix: /opt/conda/envs/conan

conans/test/util/tools_test.py::SVNToolTestsBasic::test_clone_over_dirty_directory conans/test/util/tools_test.py::SVNToolTestsPristine::test_missing_remote conans/test/util/tools_test.py::SVNToolTestsPristine::test_untracked_file conans/test/util/tools_test.py::SVNToolTestsPristineWithExternalFile::test_modified_external conans/test/util/tools_test.py::SVNToolTestsPristineWithExternalsNotFixed::test_modified_external conans/test/util/tools_test.py::SVNToolTestsPristineWithExternalsFixed::test_commit_external conans/test/util/tools_test.py::SVNToolTestsPristineWithExternalsFixed::test_ignored_external conans/test/util/tools_test.py::SVNToolTestsPristineWithExternalsFixed::test_modified_external conans/test/util/tools_test.py::SVNToolTestsPristineWithExternalsFixed::test_untracked_external
conans/test/util/tools_test.py::ToolsTest::test_get_env_in_conanfile conans/test/util/tools_test.py::ToolsTest::test_global_tools_overrided conans/test/util/tools_test.py::GitToolTest::test_clone_submodule_git conans/test/util/tools_test.py::SVNToolsTestsRecipe::test_clone_root_folder conans/test/util/tools_test.py::SVNToolsTestsRecipe::test_clone_subfolder
conans/test/util/tools_test.py::SystemPackageToolTest::test_sudo_tty conans/test/util/tools_test.py::ReplaceInFileTest::test_replace_in_file conans/test/util/tools_test.py::ToolsTest::test_environment_nested conans/test/util/tools_test.py::GitToolTest::test_clone_existing_folder_git conans/test/util/tools_test.py::GitToolTest::test_clone_existing_folder_without_branch conans/test/util/tools_test.py::GitToolTest::test_clone_git conans/test/util/tools_test.py::GitToolTest::test_credentials conans/test/util/tools_test.py::GitToolTest::test_is_local_repository conans/test/util/tools_test.py::GitToolTest::test_is_pristine conans/test/util/tools_test.py::GitToolTest::test_repo_root conans/test/util/tools_test.py::GitToolTest::test_verify_ssl conans/test/util/tools_test.py::SVNToolTestsBasic::test_branch conans/test/util/tools_test.py::SVNToolTestsBasic::test_checkout conans/test/util/tools_test.py::SVNToolTestsBasic::test_clone conans/test/util/tools_test.py::SVNToolTestsBasic::test_credentials conans/test/util/tools_test.py::SVNToolTestsBasic::test_excluded_files conans/test/util/tools_test.py::SVNToolTestsBasic::test_is_local_repository conans/test/util/tools_test.py::SVNToolTestsBasic::test_last_changed_revision conans/test/util/tools_test.py::SVNToolTestsBasic::test_repo_project_url conans/test/util/tools_test.py::SVNToolTestsBasic::test_repo_root conans/test/util/tools_test.py::SVNToolTestsBasic::test_repo_url conans/test/util/tools_test.py::SVNToolTestsBasic::test_revision_number conans/test/util/tools_test.py::SVNToolTestsBasic::test_verify_ssl conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_branch conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_checkout conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_clone conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_clone_over_dirty_directory conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_credentials conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_excluded_files conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_is_local_repository conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_last_changed_revision conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_repo_project_url conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_repo_root conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_repo_url conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_revision_number conans/test/util/tools_test.py::SVNToolTestsBasicOldVersion::test_verify_ssl conans/test/util/tools_test.py::SVNToolTestsPristine::test_checkout conans/test/util/tools_test.py::SVNToolTestsPristine::test_checkout_project conans/test/util/tools_test.py::SVNToolTestsPristine::test_conflicted_file conans/test/util/tools_test.py::SVNToolTestsPristine::test_ignored_file conans/test/util/tools_test.py::SVNToolTestsPristine::test_mixed_revisions conans/test/util/tools_test.py::SVNToolTestsPristine::test_modified_file
conans/client/tools/scm.py
conans/client/tools/scm.py