Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Checking some edge cases on diff action dealing with directories vs f…

…iles.
  • Loading branch information...
commit ae3fda64a1aad6c7d40b343a98a1239305a6fda9 1 parent f1aad2d
@fabioz authored
View
76 mu_repo/action_diff.py
@@ -10,6 +10,23 @@
import subprocess
from mu_repo.execute_git_command_in_thread import ExecuteGitCommandThread
from Queue import Queue
+from mu_repo.rmtree import RmTree
+
+#Erorr listeners may add themselves here (activated when errors happen in a worker thread).
+on_errors_listeners = set()
+
+#===================================================================================================
+# NotifyErrorListeners
+#===================================================================================================
+def NotifyErrorListeners():
+ import StringIO
+ import traceback
+ cstr = StringIO.StringIO()
+ traceback.print_exc(file=cstr)
+ error = cstr.getvalue()
+ for listener in on_errors_listeners:
+ listener(error)
+ Print(error)
#===================================================================================================
@@ -19,7 +36,7 @@ def ExecuteGettingStdOutput(cmd, cwd):
p = subprocess.Popen(
cmd,
cwd=cwd,
- stderr=subprocess.STDOUT,
+ #stderr=subprocess.STDOUT, # -- let stderr go to sys.stderr!
stdout=subprocess.PIPE,
stdin=subprocess.PIPE
)
@@ -28,7 +45,6 @@ def ExecuteGettingStdOutput(cmd, cwd):
return stdout
-
#===================================================================================================
# CreateFromGit
#===================================================================================================
@@ -40,11 +56,19 @@ def __init__(self, *args):
self._args = args
def __call__(self):
- git, repo, original_repo, target_repo = self._args
- stdout = ExecuteGettingStdOutput(
- '%s show HEAD:%s' % (git, original_repo,), repo)
- with open(target_repo, 'wb') as f:
- f.write(stdout)
+ try:
+ git, repo, original_repo, target_repo = self._args
+ stdout = ExecuteGettingStdOutput(
+ '%s show HEAD:%s' % (git, original_repo,), repo)
+
+ try:
+ if not os.path.isdir(target_repo):
+ with open(target_repo, 'wb') as f:
+ f.write(stdout)
+ except:
+ print 'Error writing to file: %s\n%s' % (target_repo, stdout,)
+ except:
+ NotifyErrorListeners()
#===================================================================================================
# Symlink
@@ -57,11 +81,14 @@ def __init__(self, *args):
self._args = args
def __call__(self):
- symlink, original, link = self._args
- symlink(
- original,
- link
- )
+ try:
+ symlink, original, link = self._args
+ symlink(
+ original,
+ link
+ )
+ except:
+ NotifyErrorListeners()
#===================================================================================================
@@ -152,7 +179,10 @@ def __init__(self, config, repo, symlink, temp_working, temp_repo):
def run(self):
- ExecuteGitCommandThread.run(self, serial=False)
+ try:
+ ExecuteGitCommandThread.run(self, serial=False)
+ except:
+ NotifyErrorListeners()
def _HandleOutput(self, msg, stdout):
@@ -195,7 +225,7 @@ def Run(params):
(temp_dir_name,)
).strip().lower()
if n == 'y':
- shutil.rmtree(temp_dir_name)
+ RmTree(temp_dir_name)
break
if n == 'n':
Print('Canceling diff action.')
@@ -228,8 +258,20 @@ def symlink(src, target):
except:
from mu_repo import keep_files_synched
def symlink(src, target):
- shutil.copyfile(src, target)
- keep_files_synched.KeepInSync(src, target)
+ if os.path.isdir(src):
+ if os.path.exists(target):
+ os.rmdir(target)
+ shutil.copytree(src, target)
+ #This print is a TODO!
+ Print('WARNING: TODO: Structure from: %s is copied (and not symlinked nor kept in sync).' % (target,))
+ else:
+ if os.path.exists(target):
+ if os.path.isdir(target):
+ RmTree(target)
+ else:
+ os.remove(target)
+ shutil.copyfile(src, target)
+ keep_files_synched.KeepInSync(src, target)
try:
#Note: we could use diff status --porcelain instead if we wanted to check untracked files.
@@ -259,7 +301,7 @@ def symlink(src, target):
finally:
def onerror(*args):
Print('Error removing temporary directory structure: %s' % (args,))
- shutil.rmtree(temp_dir_name, onerror=onerror)
+ RmTree(temp_dir_name, onerror=onerror)
View
2  mu_repo/execute_git_command_in_thread.py
@@ -40,7 +40,7 @@ def run(self, serial=False):
p = subprocess.Popen(
cmd,
cwd=repo,
- stderr=subprocess.STDOUT,
+ #stderr=subprocess.STDOUT, # -- let stderr go to sys.stderr!
stdout=subprocess.PIPE,
stdin=subprocess.PIPE
)
View
37 mu_repo/rmtree.py
@@ -0,0 +1,37 @@
+'''
+Created on May 23, 2012
+
+@author: Fabio Zadrozny
+'''
+import shutil
+import os
+
+#===================================================================================================
+# _OnError
+#===================================================================================================
+def _OnError(func, path, exc_info):
+ """
+ See: http://stackoverflow.com/questions/2656322/python-shutil-rmtree-fails-on-windows-with-access-is-denied
+
+ Error handler for ``shutil.rmtree``.
+
+ If the error is due to an access error (read only file)
+ it attempts to add write permission and then retries.
+
+ If the error is for another reason it re-raises the error.
+
+ Usage : ``shutil.rmtree(path, _OnError=_OnError)``
+ """
+ import stat
+ if not os.access(path, os.W_OK):
+ # Is the error an access error ?
+ os.chmod(path, stat.S_IWUSR)
+ func(path)
+ else:
+ raise
+
+#===================================================================================================
+# RmTree
+#===================================================================================================
+def RmTree(path, ignore_errors=False, onerror=None):
+ return shutil.rmtree(path, ignore_errors, onerror or _OnError)
View
100 mu_repo/tests/test_diff_command.py
@@ -0,0 +1,100 @@
+'''
+Created on May 23, 2012
+
+@author: Fabio Zadrozny
+'''
+import unittest
+import os.path
+from mu_repo.config import Config
+from mu_repo import action_diff, Params
+import shutil
+import subprocess
+import sys
+import time
+from mu_repo.rmtree import RmTree
+
+
+#===================================================================================================
+# Test
+#===================================================================================================
+class Test(unittest.TestCase):
+
+
+ def setUp(self):
+ unittest.TestCase.setUp(self)
+ self.clear()
+
+
+ def tearDown(self):
+ unittest.TestCase.tearDown(self)
+ self.clear()
+
+
+ def clear(self):
+ if os.path.exists('test_diff_command_git_repo_dir'):
+ try:
+ RmTree('test_diff_command_git_repo_dir')
+ except:
+ time.sleep(1)
+ RmTree('test_diff_command_git_repo_dir')
+
+
+ def CallDiff(self):
+ config = Config(repos=['test_diff_command_git_repo_dir'], git=self.git)
+ params = Params(config, ['dd'], config_file=None, stream=sys.stdout)
+
+ called = []
+ def Call(cmd, *args, **kwargs):
+ called.append(cmd[0].lower())
+
+ errors = []
+ def OnError(error):
+ errors.append(error)
+
+ #Mock things
+ original_call = subprocess.call
+ subprocess.call = Call
+ action_diff.on_errors_listeners.add(OnError)
+ try:
+ action_diff.Run(params)
+ finally:
+ action_diff.on_errors_listeners.remove(OnError)
+ subprocess.call = original_call
+ if errors:
+ self.fail('\n\n'.join(errors))
+ return called
+
+
+
+ def testActionDiff(self):
+ temp_dir = 'test_diff_command_git_repo_dir'
+ git = r'C:\D\bin\git\bin\git.exe'
+ if not os.path.exists(git):
+ git = 'git'
+ self.git = git
+
+ #1. Test diffing with new folder structure
+ subprocess.call([git] + 'init test_diff_command_git_repo_dir'.split(), cwd='.')
+ os.mkdir(os.path.join(temp_dir, 'folder1'))
+ with open(os.path.join(temp_dir, 'folder1', 'out.txt'), 'w') as f:
+ f.write('out')
+ called = self.CallDiff()
+
+ self.assertEqual(['winmergeu.exe'], called)
+
+ #2. Test diffing dir structure in git changed for file in working dir
+ subprocess.call([git] + 'add -A'.split(), cwd=temp_dir)
+ subprocess.call([git] + 'commit -m "First'.split(), cwd=temp_dir)
+ RmTree(os.path.join(temp_dir, 'folder1'))
+ with open(os.path.join(temp_dir, 'folder1'), 'w') as f:
+ f.write('folder1 is now file.')
+
+ called = self.CallDiff()
+ self.assertEqual(['winmergeu.exe'], called)
+
+#===================================================================================================
+# main
+#===================================================================================================
+if __name__ == "__main__":
+ #import sys;sys.argv = ['', 'Test.testMuRepo']
+ unittest.main()
View
3  mu_repo/tests/test_keep_files_synched.py
@@ -7,6 +7,7 @@
import os.path
import shutil
from mu_repo import keep_files_synched
+from mu_repo.rmtree import RmTree
#===================================================================================================
@@ -26,7 +27,7 @@ def tearDown(self):
def clear(self):
if os.path.exists('.test_temp_dir'):
- shutil.rmtree('.test_temp_dir')
+ RmTree('.test_temp_dir')
def read(self, file2):
Please sign in to comment.
Something went wrong with that request. Please try again.