diff --git a/gitFunctions.py b/gitFunctions.py index 9c98384..5d0160a 100644 --- a/gitFunctions.py +++ b/gitFunctions.py @@ -191,14 +191,17 @@ def promotebranchtomaster(branchname): shouter.shout("Branch %s couldnt get renamed to master, please do that on your own" % branchname) return 1 # branch couldnt get renamed + @staticmethod + def get_untracked_statuszlines(): + return shell.getoutput("git status --untracked-files=all -z", stripped=False) + + @staticmethod def handleignore(): """ check untracked files and handle both global and local ignores """ - # make sure we see all untracked files: - lines = shell.getoutput("git status --untracked-files=all -z", stripped=False) - repositoryfiles = Commiter.splitoutputofgitstatusz(lines) + repositoryfiles = Commiter.splitoutputofgitstatusz(Commiter.get_untracked_statuszlines()) Commiter.ignoreextensions(repositoryfiles) Commiter.ignorejazzignore(repositoryfiles) @@ -267,6 +270,24 @@ def translatejazzignore(jazzignorelines): gitignorelines.append(gitignoreline) return gitignorelines + @staticmethod + def restore_shed_gitignore(statuszlines): + """ + If a force reload of the RTC workspace sheds .gitignore files away, we need to restore them. + In this case they are marked as deletions from git. + + :param statuszlines: the git status z output lines + """ + gitignore = ".gitignore" + gitignorelen = len(gitignore) + deletedfiles = Commiter.splitoutputofgitstatusz(statuszlines, " D ") + for deletedfile in deletedfiles: + if deletedfile[-gitignorelen:] == gitignore: + # only restore .gitignore if sibling .jazzignore still exists + jazzignorefile = deletedfile[:-gitignorelen] + ".jazzignore" + if os.path.exists(jazzignorefile): + shell.execute("git checkout -- %s" % deletedfile) + @staticmethod def ignorejazzignore(repositoryfiles): """ diff --git a/rtcFunctions.py b/rtcFunctions.py index 60e89d0..4a387bb 100644 --- a/rtcFunctions.py +++ b/rtcFunctions.py @@ -60,6 +60,8 @@ def load(self): shouter.shout("Start (re)loading current workspace: " + command) shell.execute(command) shouter.shout("Load of workspace finished") + Commiter.restore_shed_gitignore(Commiter.get_untracked_statuszlines()) + def setcomponentstobaseline(self, componentbaselineentries, streamuuid): for entry in componentbaselineentries: diff --git a/tests/resources/test_ignore_git_status_z.txt b/tests/resources/test_ignore_git_status_z.txt index b250586..9094fdd 100644 Binary files a/tests/resources/test_ignore_git_status_z.txt and b/tests/resources/test_ignore_git_status_z.txt differ diff --git a/tests/test_gitFunctions.py b/tests/test_gitFunctions.py index f8cf6a7..148a817 100644 --- a/tests/test_gitFunctions.py +++ b/tests/test_gitFunctions.py @@ -1,7 +1,7 @@ import unittest import os import time -from unittest.mock import patch +from unittest.mock import patch, call import datetime import shell @@ -171,7 +171,7 @@ def test_CopyBranch_TargetAlreadyExist_ShouldFail(self): def test_splitoutputofgitstatusz(self): with open(testhelper.getrelativefilename('./resources/test_ignore_git_status_z.txt'), 'r') as file: repositoryfiles = Commiter.splitoutputofgitstatusz(file.readlines()) - self.assertEqual(13, len(repositoryfiles)) + self.assertEqual(15, len(repositoryfiles)) self.assertEqual('project1/src/tobedeleted.txt', repositoryfiles[0]) self.assertEqual('project2/src/taka.txt', repositoryfiles[1]) self.assertEqual('project1/src/taka.txt', repositoryfiles[2]) # rename continuation would bite here @@ -185,6 +185,8 @@ def test_splitoutputofgitstatusz(self): self.assertEqual('project1/src/sub/kling |and| klong.zip', repositoryfiles[10]) self.assertEqual('project1/src/sub/klingklong.zip', repositoryfiles[11]) self.assertEqual('project1/src/sub/.jazzignore', repositoryfiles[12]) + self.assertEqual('project1/src/.gitignore', repositoryfiles[13]) + self.assertEqual('project1/src/sub/.gitignore', repositoryfiles[14]) def test_splitoutputofgitstatusz_filterprefix_A(self): with open(testhelper.getrelativefilename('./resources/test_ignore_git_status_z.txt'), 'r') as file: @@ -192,6 +194,14 @@ def test_splitoutputofgitstatusz_filterprefix_A(self): self.assertEqual(1, len(repositoryfiles)) self.assertEqual('project1/src/tobedeleted.txt', repositoryfiles[0]) + def test_splitoutputofgitstatusz_filterprefix_D(self): + with open(testhelper.getrelativefilename('./resources/test_ignore_git_status_z.txt'), 'r') as file: + repositoryfiles = Commiter.splitoutputofgitstatusz(file.readlines(), ' D ') + self.assertEqual(3, len(repositoryfiles)) + self.assertEqual('project1/src/sub/.jazzignore', repositoryfiles[0]) + self.assertEqual('project1/src/.gitignore', repositoryfiles[1]) + self.assertEqual('project1/src/sub/.gitignore', repositoryfiles[2]) + def test_splitoutputofgitstatusz_filterprefix_double_question(self): with open(testhelper.getrelativefilename('./resources/test_ignore_git_status_z.txt'), 'r') as file: repositoryfiles = Commiter.splitoutputofgitstatusz(file.readlines(), '?? ') @@ -204,6 +214,22 @@ def test_splitoutputofgitstatusz_filterprefix_double_question(self): self.assertEqual('project1/src/sub/kling |and| klong.zip', repositoryfiles[5]) self.assertEqual('project1/src/sub/klingklong.zip', repositoryfiles[6]) + @patch('gitFunctions.shell') + def test_restore_shed_gitignore_with_sibling_jazzignore(self, shellmock): + with open(testhelper.getrelativefilename('./resources/test_ignore_git_status_z.txt'), 'r') as file: + with patch('os.path.exists', return_value=True): # answer inquries for sibling .jazzignore with True + Commiter.restore_shed_gitignore(file.readlines()) + calls = [call.execute('git checkout -- project1/src/.gitignore'), call.execute('git checkout -- project1/src/sub/.gitignore')] + shellmock.assert_has_calls(calls) + + @patch('gitFunctions.shell') + def test_restore_shed_gitignore_without_sibling_jazzignore(self, shellmock): + with open(testhelper.getrelativefilename('./resources/test_ignore_git_status_z.txt'), 'r') as file: + with patch('os.path.exists', return_value=True): # answer inquries for sibling .jazzignore with False + Commiter.restore_shed_gitignore(file.readlines()) + calls = [] # if there are no siblings, we are not allowed to checkout + shellmock.assert_has_calls(calls) + def test_handleignore_global_extensions(self): with testhelper.mkchdir("aFolder") as folder: # create test repo @@ -374,7 +400,6 @@ def test_translatejazzignore(self): expectedlines = gitignore.readlines() self.assertEqual(expectedlines, Commiter.translatejazzignore(inputlines)) - def simulateCreationAndRenameInGitRepo(self, originalfilename, newfilename): open(originalfilename, 'a').close() # create file Initializer.initialcommit() diff --git a/tests/test_rtcFunctions.py b/tests/test_rtcFunctions.py index 9973ac8..21d1a5e 100644 --- a/tests/test_rtcFunctions.py +++ b/tests/test_rtcFunctions.py @@ -1,6 +1,6 @@ import unittest import os -from unittest.mock import patch +from unittest.mock import patch, call import configuration import shell @@ -236,22 +236,29 @@ def test_useragreeing_answeris_n_expectfalseandexception(self, inputmock): self.assertEqual("Please check the output/log and rerun program with resume", e.code) @patch('rtcFunctions.shell') - def test_load(self, shellmock): + @patch('rtcFunctions.Commiter') + def test_load(self, commitermock, shellmock): anyurl = "anyUrl" config = self.configBuilder.setrepourl(anyurl).setworkspace(self.workspace).build() configuration.config = config WorkspaceHandler().load() expected_load_command = "lscm load -r %s %s --force" % (anyurl, self.workspace) shellmock.execute.assert_called_once_with(expected_load_command) + calls = [call.get_untracked_statuszlines(), call.restore_shed_gitignore(commitermock.get_untracked_statuszlines())] + commitermock.assert_has_calls(calls) @patch('rtcFunctions.shell') - def test_load_includecomponentroots(self, shellmock): + @patch('rtcFunctions.Commiter') + def test_load_includecomponentroots(self, commitermock, shellmock): anyurl = "anyUrl" config = self.configBuilder.setrepourl(anyurl).setworkspace(self.workspace).setincludecomponentroots("True").build() configuration.config = config WorkspaceHandler().load() expected_load_command = "lscm load -r %s %s --force --include-root" % (anyurl, self.workspace) shellmock.execute.assert_called_once_with(expected_load_command) + shellmock.execute.assert_called_once_with(expected_load_command) + calls = [call.get_untracked_statuszlines(), call.restore_shed_gitignore(commitermock.get_untracked_statuszlines())] + commitermock.assert_has_calls(calls) def test_CreateChangeEntry_minimal(self): revision="anyRevisionId"