def test_lint_sample3(self): linter = GitLinter(LintConfig()) gitcontext = GitContext() gitcontext.set_commit_msg(self.get_sample("commit_message/sample3")) violations = linter.lint(gitcontext) title = " Commit title containing 'WIP', \tleading and trailing whitespace and longer than 72 characters." expected = [ RuleViolation("T1", "Title exceeds max length (95>72)", title, 1), RuleViolation("T3", "Title has trailing punctuation (.)", title, 1), RuleViolation("T4", "Title contains hard tab characters (\\t)", title, 1), RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", title, 1), RuleViolation("T6", "Title has leading whitespace", title, 1), RuleViolation("B4", "Second line is not empty", "This line should be empty", 2), RuleViolation( "B1", "Line exceeds max length (101>80)", "This is the first line is meant to test a line that exceeds the maximum line " + "length of 80 characters.", 3, ), RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing space. ", 4), RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing tab.\t", 5), RuleViolation("B3", "Line contains hard tab characters (\\t)", "This line has a trailing tab.\t", 5), ] self.assertListEqual(violations, expected)
def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj[0] try: if sys.stdin.isatty(): # If target has not been set explicitly before, fallback to the current directory gitcontext = GitContext.from_local_repository(lint_config.target) else: gitcontext = GitContext.from_commit_msg(sys.stdin.read()) except GitContextError as e: click.echo(str(e)) ctx.exit(GIT_CONTEXT_ERROR_CODE) config_builder = ctx.obj[1] last_commit = gitcontext.commits[-1] # Apply an additional config that is specified in the last commit message config_builder.set_config_from_commit(last_commit) lint_config = config_builder.build(lint_config) # Let's get linting! linter = GitLinter(lint_config) violations = linter.lint(last_commit) linter.print_violations(violations) exit_code = min(MAX_VIOLATION_ERROR_CODE, len(violations)) ctx.exit(exit_code)
def test_gitcontext_ignore_all(self): config = LintConfig() original_rules = config.rules # nothing gitlint context = GitContext() context.set_commit_msg("test\ngitlint\nfoo") config.apply_config_from_gitcontext(context) self.assertListEqual(config.rules, original_rules) # ignore all rules context = GitContext() context.set_commit_msg("test\ngitlint-ignore: all\nfoo") config.apply_config_from_gitcontext(context) self.assertEqual(config.rules, []) # ignore all rules, no space config = LintConfig() context.set_commit_msg("test\ngitlint-ignore:all\nfoo") config.apply_config_from_gitcontext(context) self.assertEqual(config.rules, []) # ignore all rules, more spacing config = LintConfig() context.set_commit_msg("test\ngitlint-ignore: \t all\nfoo") config.apply_config_from_gitcontext(context) self.assertEqual(config.rules, [])
def cli(config, c, ignore, verbose, silent): """ Git lint tool, checks your git commit messages for styling issues """ try: # Config precedence: # First, load default config or config from configfile lint_config = get_lint_config(config) # Then process any commandline configuration flags try: lint_config.apply_config_options(c) except LintConfigError as e: click.echo("Config Error: {}".format(e.message)) exit(CONFIG_ERROR_CODE) # Finally, overwrite with any convenience commandline flags lint_config.apply_on_csv_string(ignore, lint_config.disable_rule) if silent: lint_config.verbosity = 0 elif verbose > 0: lint_config.verbosity = verbose except LintConfigError as e: click.echo("Config Error: {0}".format(e.message)) exit(CONFIG_ERROR_CODE) # return 10000 on config error if sys.stdin.isatty(): gitcontext = GitContext.from_environment() else: gitcontext = GitContext() gitcontext.set_commit_msg(sys.stdin.read()) linter = GitLinter(lint_config) violations = linter.lint(gitcontext) linter.print_violations(violations) exit(len(violations))
def test_lint_sample1(self): linter = GitLinter(LintConfig()) gitcontext = GitContext() gitcontext.set_commit_msg(self.get_sample("commit_message/sample1")) violations = linter.lint(gitcontext) expected_errors = [ RuleViolation( "T3", "Title has trailing punctuation (.)", "Commit title containing 'WIP', as well as trailing punctuation.", 1, ), RuleViolation( "T5", "Title contains the word 'WIP' (case-insensitive)", "Commit title containing 'WIP', as well as trailing punctuation.", 1, ), RuleViolation("B4", "Second line is not empty", "This line should be empty", 2), RuleViolation( "B1", "Line exceeds max length (135>80)", "This is the first line of the commit message body and it is meant to test " + "a line that exceeds the maximum line length of 80 characters.", 3, ), RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing space. ", 4), RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing tab.\t", 5), RuleViolation("B3", "Line contains hard tab characters (\\t)", "This line has a trailing tab.\t", 5), ] self.assertListEqual(violations, expected_errors)
def test_set_commit_msg_just_title(self): gitcontext = GitContext() gitcontext.set_commit_msg(self.get_sample("commit_message/sample2")) self.assertEqual(gitcontext.commit_msg.title, "Just a title containing WIP") self.assertEqual(gitcontext.commit_msg.body, []) self.assertEqual(gitcontext.commit_msg.full, "Just a title containing WIP") self.assertEqual(gitcontext.commit_msg.original, "Just a title containing WIP")
def test_gitcontext_ignore_specific(self): # ignore specific rules config = LintConfig() context = GitContext() context.set_commit_msg("test\ngitlint-ignore: T1, body-hard-tab") config.apply_config_from_gitcontext(context) expected_rules = [rule for rule in config.rules if rule.id not in ["T1", "body-hard-tab"]] self.assertEqual(config.rules, expected_rules)
def test_get_latest_commit_git_error(self, sh): err = b"fatal: Not a git repository (or any of the parent directories): .git" sh.git.log.side_effect = ErrorReturnCode("git log -1 --pretty=%B", b"", err) with self.assertRaisesRegexp(GitContextError, "fake/path is not a git repository."): GitContext.from_local_repository("fake/path") # assert that commit message was read using git command sh.git.log.assert_called_once_with('-1', '--pretty=%B', _tty_out=False, _cwd="fake/path")
def test_get_latest_commit_command_not_found(self, sh): sh.git.log.side_effect = CommandNotFound("git") expected_msg = "'git' command not found. You need to install git to use gitlint on a local repository. " + \ "See https://git-scm.com/book/en/v2/Getting-Started-Installing-Git on how to install git." with self.assertRaisesRegexp(GitContextError, expected_msg): GitContext.from_local_repository("fake/path") # assert that commit message was read using git command sh.git.log.assert_called_once_with('-1', '--pretty=%B', _tty_out=False, _cwd="fake/path")
def test_lint_sample2(self): linter = GitLinter(LintConfig()) gitcontext = GitContext() gitcontext.set_commit_msg(self.get_sample("commit_message/sample2")) violations = linter.lint(gitcontext) expected = [RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", "Just a title containing WIP", 1), RuleViolation("B6", "Body message is missing", None, 3)] self.assertListEqual(violations, expected)
def gitcontext(commit_msg_str, changed_files=None): """ Utility method to easily create gitcontext objects based on a given commit msg string and set of changed files""" gitcontext = GitContext() gitcontext.set_commit_msg(commit_msg_str) if changed_files: gitcontext.changed_files = changed_files else: gitcontext.changed_files = [] return gitcontext
def test_lint_sample4(self): gitcontext = GitContext() gitcontext.set_commit_msg(self.get_sample("commit_message/sample4")) lintconfig = LintConfig() lintconfig.apply_config_from_gitcontext(gitcontext) linter = GitLinter(lintconfig) violations = linter.lint(gitcontext) # expect no violations because sample4 has a 'gitlint: disable line' expected = [] self.assertListEqual(violations, expected)
def test_get_latest_commit(self, sh): def git_log_side_effect(*args, **kwargs): return_values = {'--pretty=%B': "commit-title\n\ncommit-body", '--pretty=%aN': "test author", '--pretty=%aE': "*****@*****.**", '--pretty=%aD': "Mon Feb 29 22:19:39 2016 +0100"} return return_values[args[1]] sh.git.log.side_effect = git_log_side_effect sh.git.return_value = "file1.txt\npath/to/file2.txt\n" context = GitContext.from_local_repository("fake/path") expected_sh_special_args = { '_tty_out': False, '_cwd': "fake/path" } # assert that commit info was read using git command expected_calls = [call('-1', '--pretty=%B', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%aN', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%aE', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%aD', _cwd='fake/path', _tty_out=False)] self.assertListEqual(sh.git.log.mock_calls, expected_calls) last_commit = context.commits[-1] self.assertEqual(last_commit.message.title, "commit-title") self.assertEqual(last_commit.message.body, ["", "commit-body"]) self.assertEqual(last_commit.author_name, "test author") self.assertEqual(last_commit.author_email, "*****@*****.**") # assert that changed files are read using git command sh.git.assert_called_once_with('diff-tree', '--no-commit-id', '--name-only', '-r', 'HEAD', **expected_sh_special_args) self.assertListEqual(last_commit.changed_files, ["file1.txt", "path/to/file2.txt"])
def gitcontext(commit_msg_str, changed_files=None): """ Utility method to easily create gitcontext objects based on a given commit msg string and set of changed files""" gitcontext = GitContext.from_commit_msg(commit_msg_str) commit = gitcontext.commits[-1] if changed_files: commit.changed_files = changed_files return gitcontext
def test_set_commit_msg_full(self): gitcontext = GitContext() gitcontext.set_commit_msg(self.get_sample("commit_message/sample1")) expected_title = "Commit title containing 'WIP', as well as trailing punctuation." expected_body = ["This line should be empty", "This is the first line of the commit message body and it is meant to test a " + "line that exceeds the maximum line length of 80 characters.", "This line has a trailing space. ", "This line has a trailing tab.\t", ""] expected_full = expected_title + "\n" + "\n".join(expected_body) expected_original = expected_full + "# This is a commented line\n" self.assertEqual(gitcontext.commit_msg.title, expected_title) self.assertEqual(gitcontext.commit_msg.body, expected_body) self.assertEqual(gitcontext.commit_msg.full, expected_full) self.assertEqual(gitcontext.commit_msg.original, expected_original)
def test_from_commit_msg_empty(self): gitcontext = GitContext.from_commit_msg("") self.assertEqual(gitcontext.commits[-1].message.title, "") self.assertEqual(gitcontext.commits[-1].message.body, []) self.assertEqual(gitcontext.commits[-1].message.full, "") self.assertEqual(gitcontext.commits[-1].message.original, "") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_comment(self): gitcontext = GitContext.from_commit_msg("Title\n\nBody 1\n#Comment\nBody 2") self.assertEqual(gitcontext.commits[-1].message.title, "Title") self.assertEqual(gitcontext.commits[-1].message.body, ["", "Body 1", "Body 2"]) self.assertEqual(gitcontext.commits[-1].message.full, "Title\n\nBody 1\nBody 2") self.assertEqual(gitcontext.commits[-1].message.original, "Title\n\nBody 1\n#Comment\nBody 2") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertEqual(len(gitcontext.commits), 1)
def test_set_commit_msg_just_title(self): gitcontext = GitContext.from_commit_msg(self.get_sample("commit_message/sample2")) self.assertEqual(gitcontext.commits[-1].message.title, "Just a title containing WIP") self.assertEqual(gitcontext.commits[-1].message.body, []) self.assertEqual(gitcontext.commits[-1].message.full, "Just a title containing WIP") self.assertEqual(gitcontext.commits[-1].message.original, "Just a title containing WIP") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertEqual(len(gitcontext.commits), 1)
def test_lint_sample5(self): gitcontext = GitContext() gitcontext.set_commit_msg(self.get_sample("commit_message/sample5")) lintconfig = LintConfig() lintconfig.apply_config_from_gitcontext(gitcontext) linter = GitLinter(lintconfig) violations = linter.lint(gitcontext) title = " Commit title containing 'WIP', \tleading and trailing whitespace and longer than 72 characters." # expect only certain violations because sample5 has a 'gitlint: T3,' expected = [RuleViolation("T1", "Title exceeds max length (95>72)", title, 1), RuleViolation("T4", "Title contains hard tab characters (\\t)", title, 1), RuleViolation("T5", "Title contains the word 'WIP' (case-insensitive)", title, 1), RuleViolation("B4", "Second line is not empty", "This line should be empty", 2), RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing space. ", 4), RuleViolation("B2", "Line has trailing whitespace", "This line has a trailing tab.\t", 5), RuleViolation("B3", "Line contains hard tab characters (\\t)", "This line has a trailing tab.\t", 5), ] self.assertListEqual(violations, expected)
def test_get_latest_commit_git_error(self, sh): # Current directory not a git repo err = b"fatal: Not a git repository (or any of the parent directories): .git" sh.git.log.side_effect = ErrorReturnCode("git log -1 --pretty=%B", b"", err) with self.assertRaisesRegex(GitContextError, "fake/path is not a git repository."): GitContext.from_local_repository("fake/path") # assert that commit message was read using git command sh.git.log.assert_called_once_with('-1', '--pretty=%B', _tty_out=False, _cwd="fake/path") sh.git.log.reset_mock() err = b"fatal: Random git error" sh.git.log.side_effect = ErrorReturnCode("git log -1 --pretty=%B", b"", err) expected_msg = "An error occurred while executing 'git log -1 --pretty=%B': {0}".format(err) with self.assertRaisesRegex(GitContextError, expected_msg): GitContext.from_local_repository("fake/path") # assert that commit message was read using git command sh.git.log.assert_called_once_with('-1', '--pretty=%B', _tty_out=False, _cwd="fake/path")
def test_from_commit_msg_merge_commit(self): commit_msg = "Merge f919b8f34898d9b48048bcd703bc47139f4ff621 into 8b0409a26da6ba8a47c1fd2e746872a8dab15401" gitcontext = GitContext.from_commit_msg(commit_msg) self.assertEqual(gitcontext.commits[-1].message.title, commit_msg) self.assertEqual(gitcontext.commits[-1].message.body, []) self.assertEqual(gitcontext.commits[-1].message.full, commit_msg) self.assertEqual(gitcontext.commits[-1].message.original, commit_msg) self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertListEqual(gitcontext.commits[-1].parents, []) self.assertTrue(gitcontext.commits[-1].is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj try: if sys.stdin.isatty(): gitcontext = GitContext.from_local_repository(lint_config.target) else: gitcontext = GitContext.from_commit_msg(sys.stdin.read()) except GitContextError as e: click.echo(str(e)) ctx.exit(GIT_CONTEXT_ERROR_CODE) last_commit = gitcontext.commits[-1] # Apply an additional config that is specified in the last commit message lint_config.apply_config_from_commit(last_commit) # Let's get linting! linter = GitLinter(lint_config) violations = linter.lint(last_commit, gitcontext) linter.print_violations(violations) exit_code = min(MAX_VIOLATION_ERROR_CODE, len(violations)) ctx.exit(exit_code)
def test_from_commit_msg_comment(self): gitcontext = GitContext.from_commit_msg( "Title\n\nBody 1\n#Comment\nBody 2") self.assertEqual(gitcontext.commits[-1].message.title, "Title") self.assertEqual(gitcontext.commits[-1].message.body, ["", "Body 1", "Body 2"]) self.assertEqual(gitcontext.commits[-1].message.full, "Title\n\nBody 1\nBody 2") self.assertEqual(gitcontext.commits[-1].message.original, "Title\n\nBody 1\n#Comment\nBody 2") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertListEqual(gitcontext.commits[-1].parents, []) self.assertFalse(gitcontext.commits[-1].is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_just_title(self): gitcontext = GitContext.from_commit_msg( self.get_sample("commit_message/sample2")) self.assertEqual(gitcontext.commits[-1].message.title, "Just a title containing WIP") self.assertEqual(gitcontext.commits[-1].message.body, []) self.assertEqual(gitcontext.commits[-1].message.full, "Just a title containing WIP") self.assertEqual(gitcontext.commits[-1].message.original, "Just a title containing WIP") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertListEqual(gitcontext.commits[-1].parents, []) self.assertFalse(gitcontext.commits[-1].is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_comment(self): gitcontext = GitContext.from_commit_msg( u"Tïtle\n\nBödy 1\n#Cömment\nBody 2") commit = gitcontext.commits[-1] self.assertEqual(commit.message.title, u"Tïtle") self.assertEqual(commit.message.body, ["", u"Bödy 1", "Body 2"]) self.assertEqual(commit.message.full, u"Tïtle\n\nBödy 1\nBody 2") self.assertEqual(commit.message.original, u"Tïtle\n\nBödy 1\n#Cömment\nBody 2") self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertFalse(commit.is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_empty(self): gitcontext = GitContext.from_commit_msg("") commit = gitcontext.commits[-1] self.assertEqual(commit.message.title, "") self.assertEqual(commit.message.body, []) self.assertEqual(commit.message.full, "") self.assertEqual(commit.message.original, "") self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_get_latest_commit_merge_commit(self, sh): sample_sha = "d8ac47e9f2923c7f22d8668e3a1ed04eb4cdbca9" def git_log_side_effect(*_args, **_kwargs): return ( u"test åuthor,test-emå[email protected],2016-12-03 15:28:15 01:00,åbc def\n" u"Merge \"foo bår commit\"") sh.git.side_effect = [sample_sha, u"file1.txt\npåth/to/file2.txt\n"] sh.git.log.side_effect = git_log_side_effect context = GitContext.from_local_repository(u"fåke/path") expected_sh_special_args = {'_tty_out': False, '_cwd': u"fåke/path"} # assert that commit info was read using git command expected_calls = [ call("rev-list", "--max-count=1", "HEAD", **expected_sh_special_args), call.log(sample_sha, "-1", "--pretty=%aN,%aE,%ai,%P%n%B", _cwd=u"fåke/path", _tty_out=False), call('diff-tree', '--no-commit-id', '--name-only', '-r', sample_sha, **expected_sh_special_args) ] self.assertEqual(sh.git.mock_calls, expected_calls) last_commit = context.commits[-1] self.assertEqual(last_commit.message.title, u"Merge \"foo bår commit\"") self.assertEqual(last_commit.message.body, []) self.assertEqual(last_commit.author_name, u"test åuthor") self.assertEqual(last_commit.author_email, u"test-emå[email protected]") self.assertEqual( last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))) self.assertListEqual(last_commit.parents, [u"åbc", "def"]) self.assertTrue(last_commit.is_merge_commit) self.assertListEqual(last_commit.changed_files, ["file1.txt", u"påth/to/file2.txt"])
def test_from_commit_msg_merge_commit(self): commit_msg = "Merge f919b8f34898d9b48048bcd703bc47139f4ff621 into 8b0409a26da6ba8a47c1fd2e746872a8dab15401" gitcontext = GitContext.from_commit_msg(commit_msg) commit = gitcontext.commits[-1] self.assertEqual(commit.message.title, commit_msg) self.assertEqual(commit.message.body, []) self.assertEqual(commit.message.full, commit_msg) self.assertEqual(commit.message.original, commit_msg) self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertTrue(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_get_latest_commit(self, sh): sh.git.log.return_value = "commit-title\n\ncommit-body" sh.git.return_value = "file1.txt\npath/to/file2.txt\n" context = GitContext.from_local_repository("fake/path") expected_sh_special_args = { '_tty_out': False, '_cwd': "fake/path" } # assert that commit message was read using git command sh.git.log.assert_called_once_with('-1', '--pretty=%B', **expected_sh_special_args) self.assertEqual(context.commit_msg.title, "commit-title") self.assertEqual(context.commit_msg.body, ["", "commit-body"]) # assert that changed files are read using git command sh.git.assert_called_once_with('diff-tree', '--no-commit-id', '--name-only', '-r', 'HEAD', **expected_sh_special_args) self.assertListEqual(context.changed_files, ["file1.txt", "path/to/file2.txt"])
def test_from_commit_msg_just_title(self): gitcontext = GitContext.from_commit_msg( self.get_sample("commit_message/sample2")) commit = gitcontext.commits[-1] self.assertEqual(commit.message.title, u"Just a title contåining WIP") self.assertEqual(commit.message.body, []) self.assertEqual(commit.message.full, u"Just a title contåining WIP") self.assertEqual(commit.message.original, u"Just a title contåining WIP") self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertListEqual(commit.parents, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertFalse(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_get_latest_commit_merge_commit(self, sh): sample_sha = "d8ac47e9f2923c7f22d8668e3a1ed04eb4cdbca9" sh.git.side_effect = self.GIT_CONFIG_SIDE_EFFECTS + [ sample_sha, u"test åuthor\x00test-emå[email protected]\x002016-12-03 15:28:15 01:00\x00åbc def\n" u"Merge \"foo bår commit\"", u"file1.txt\npåth/to/file2.txt\n" ] context = GitContext.from_local_repository(u"fåke/path") # assert that commit info was read using git command expected_calls = self.expected_git_config_calls + [ call("log", "-1", "--pretty=%H", **self.expected_sh_special_args), call("log", sample_sha, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", ** self.expected_sh_special_args), call('diff-tree', '--no-commit-id', '--name-only', '-r', sample_sha, **self.expected_sh_special_args) ] self.assertEqual(sh.git.mock_calls, expected_calls) last_commit = context.commits[-1] self.assertEqual(last_commit.message.title, u"Merge \"foo bår commit\"") self.assertEqual(last_commit.message.body, []) self.assertEqual(last_commit.author_name, u"test åuthor") self.assertEqual(last_commit.author_email, u"test-emå[email protected]") self.assertEqual( last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))) self.assertListEqual(last_commit.parents, [u"åbc", "def"]) self.assertTrue(last_commit.is_merge_commit) self.assertFalse(last_commit.is_fixup_commit) self.assertFalse(last_commit.is_squash_commit) self.assertFalse(last_commit.is_revert_commit) self.assertListEqual(last_commit.changed_files, ["file1.txt", u"påth/to/file2.txt"])
def test_from_commit_msg_full(self, commentchar): commentchar.return_value = u"#" gitcontext = GitContext.from_commit_msg( self.get_sample("commit_message/sample1")) expected_title = u"Commit title contåining 'WIP', as well as trailing punctuation." expected_body = [ "This line should be empty", "This is the first line of the commit message body and it is meant to test a " + "line that exceeds the maximum line length of 80 characters.", u"This line has a tråiling space. ", "This line has a trailing tab.\t" ] expected_full = expected_title + "\n" + "\n".join(expected_body) expected_original = expected_full + ( u"\n# This is a cömmented line\n" u"# ------------------------ >8 ------------------------\n" u"# Anything after this line should be cleaned up\n" u"# this line appears on `git commit -v` command\n" u"diff --git a/gitlint/tests/samples/commit_message/sample1 " u"b/gitlint/tests/samples/commit_message/sample1\n" u"index 82dbe7f..ae71a14 100644\n" u"--- a/gitlint/tests/samples/commit_message/sample1\n" u"+++ b/gitlint/tests/samples/commit_message/sample1\n" u"@@ -1 +1 @@\n") commit = gitcontext.commits[-1] self.assertIsInstance(commit, GitCommit) self.assertFalse(isinstance(commit, LocalGitCommit)) self.assertEqual(commit.message.title, expected_title) self.assertEqual(commit.message.body, expected_body) self.assertEqual(commit.message.full, expected_full) self.assertEqual(commit.message.original, expected_original) self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertListEqual(commit.branches, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertFalse(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_local_repository_specific_ref(self, sh): sample_sha = "myspecialref" sh.git.side_effect = [ sample_sha, u"test åuthor\x00test-emå[email protected]\x002016-12-03 15:28:15 01:00\x00åbc\n" u"cömmit-title\n\ncömmit-body", u"file1.txt\npåth/to/file2.txt\n" ] context = GitContext.from_local_repository(u"fåke/path", sample_sha) # assert that commit info was read using git command expected_calls = [ call("rev-list", sample_sha, **self.expected_sh_special_args), call("log", sample_sha, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args), call('diff-tree', '--no-commit-id', '--name-only', '-r', sample_sha, **self.expected_sh_special_args) ] self.assertListEqual(sh.git.mock_calls, expected_calls) last_commit = context.commits[-1] self.assertEqual(last_commit.message.title, u"cömmit-title") self.assertEqual(last_commit.message.body, ["", u"cömmit-body"]) self.assertEqual(last_commit.author_name, u"test åuthor") self.assertEqual(last_commit.author_email, u"test-emå[email protected]") self.assertEqual( last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))) self.assertListEqual(last_commit.parents, [u"åbc"]) self.assertFalse(last_commit.is_merge_commit) self.assertFalse(last_commit.is_fixup_commit) self.assertFalse(last_commit.is_squash_commit) self.assertListEqual(last_commit.changed_files, ["file1.txt", u"påth/to/file2.txt"])
def test_get_latest_commit(self, sh): def git_log_side_effect(*args, **_kwargs): return_values = { '--pretty=%B': "commit-title\n\ncommit-body", '--pretty=%aN': "test author", '--pretty=%aE': "*****@*****.**", '--pretty=%aD': "Mon Feb 29 22:19:39 2016 +0100", '--pretty=%P': "abc" } return return_values[args[1]] sh.git.log.side_effect = git_log_side_effect sh.git.return_value = "file1.txt\npath/to/file2.txt\n" context = GitContext.from_local_repository("fake/path") expected_sh_special_args = {'_tty_out': False, '_cwd': "fake/path"} # assert that commit info was read using git command expected_calls = [ call('-1', '--pretty=%B', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%aN', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%aE', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%aD', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%P', _cwd='fake/path', _tty_out=False) ] self.assertListEqual(sh.git.log.mock_calls, expected_calls) last_commit = context.commits[-1] self.assertEqual(last_commit.message.title, "commit-title") self.assertEqual(last_commit.message.body, ["", "commit-body"]) self.assertEqual(last_commit.author_name, "test author") self.assertEqual(last_commit.author_email, "*****@*****.**") self.assertListEqual(last_commit.parents, ["abc"]) self.assertFalse(last_commit.is_merge_commit) # assert that changed files are read using git command sh.git.assert_called_once_with('diff-tree', '--no-commit-id', '--name-only', '-r', 'HEAD', **expected_sh_special_args) self.assertListEqual(last_commit.changed_files, ["file1.txt", "path/to/file2.txt"])
def test_from_commit_msg_fixup_squash_commit(self): commit_types = ["fixup", "squash"] for commit_type in commit_types: commit_msg = "{0}! Test message".format(commit_type) gitcontext = GitContext.from_commit_msg(commit_msg) commit = gitcontext.commits[-1] self.assertEqual(commit.message.title, commit_msg) self.assertEqual(commit.message.body, []) self.assertEqual(commit.message.full, commit_msg) self.assertEqual(commit.message.original, commit_msg) self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertEqual(len(gitcontext.commits), 1) self.assertFalse(commit.is_merge_commit) # Asserting that squash and fixup are correct for type in commit_types: attr = "is_" + type + "_commit" self.assertEqual(getattr(commit, attr), commit_type == type)
def test_from_commit_msg_full(self): gitcontext = GitContext.from_commit_msg(self.get_sample("commit_message/sample1")) expected_title = "Commit title containing 'WIP', as well as trailing punctuation." expected_body = ["This line should be empty", "This is the first line of the commit message body and it is meant to test a " + "line that exceeds the maximum line length of 80 characters.", "This line has a trailing space. ", "This line has a trailing tab.\t", ""] expected_full = expected_title + "\n" + "\n".join(expected_body) expected_original = expected_full + "# This is a commented line\n" self.assertEqual(gitcontext.commits[-1].message.title, expected_title) self.assertEqual(gitcontext.commits[-1].message.body, expected_body) self.assertEqual(gitcontext.commits[-1].message.full, expected_full) self.assertEqual(gitcontext.commits[-1].message.original, expected_original) self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertListEqual(gitcontext.commits[-1].parents, []) self.assertFalse(gitcontext.commits[-1].is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_comment(self, commentchar): commentchar.return_value = "#" gitcontext = GitContext.from_commit_msg("Tïtle\n\nBödy 1\n#Cömment\nBody 2") commit = gitcontext.commits[-1] self.assertIsInstance(commit, GitCommit) self.assertFalse(isinstance(commit, LocalGitCommit)) self.assertEqual(commit.message.title, "Tïtle") self.assertEqual(commit.message.body, ["", "Bödy 1", "Body 2"]) self.assertEqual(commit.message.full, "Tïtle\n\nBödy 1\nBody 2") self.assertEqual(commit.message.original, "Tïtle\n\nBödy 1\n#Cömment\nBody 2") self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertListEqual(commit.branches, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertFalse(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_revert_commit(self): commit_msg = "Revert \"Prev commit message\"\n\nThis reverts commit a8ad67e04164a537198dea94a4fde81c5592ae9c." gitcontext = GitContext.from_commit_msg(commit_msg) commit = gitcontext.commits[-1] self.assertIsInstance(commit, GitCommit) self.assertFalse(isinstance(commit, LocalGitCommit)) self.assertEqual(commit.message.title, "Revert \"Prev commit message\"") self.assertEqual(commit.message.body, ["", "This reverts commit a8ad67e04164a537198dea94a4fde81c5592ae9c."]) self.assertEqual(commit.message.full, commit_msg) self.assertEqual(commit.message.original, commit_msg) self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertListEqual(commit.branches, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertTrue(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_gitcontext(self, sh): sh.git.side_effect = [ "#", # git config --get core.commentchar "\nfoöbar\n" ] expected_calls = [ call("config", "--get", "core.commentchar", _ok_code=[0, 1], **self.expected_sh_special_args), call("rev-parse", "--abbrev-ref", "HEAD", **self.expected_sh_special_args) ] context = GitContext("fåke/path") self.assertEqual(sh.git.mock_calls, []) # gitcontext.comment_branch self.assertEqual(context.commentchar, "#") self.assertEqual(sh.git.mock_calls, expected_calls[0:1]) # gitcontext.current_branch self.assertEqual(context.current_branch, "foöbar") self.assertEqual(sh.git.mock_calls, expected_calls)
def test_get_latest_commit_merge_commit(self, sh): def git_log_side_effect(*args, **_kwargs): return_values = {'--pretty=%B': "Merge \"foo bar commit\"", '--pretty=%aN': "test author", '--pretty=%aE': "*****@*****.**", '--pretty=%ai': "2016-12-03 15:28:15 01:00", '--pretty=%P': "abc def"} return return_values[args[1]] sh.git.log.side_effect = git_log_side_effect sh.git.return_value = "file1.txt\npath/to/file2.txt\n" context = GitContext.from_local_repository("fake/path") expected_sh_special_args = { '_tty_out': False, '_cwd': "fake/path" } # assert that commit info was read using git command expected_calls = [call('-1', '--pretty=%B', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%aN', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%aE', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%ai', _cwd='fake/path', _tty_out=False), call('-1', '--pretty=%P', _cwd='fake/path', _tty_out=False)] self.assertListEqual(sh.git.log.mock_calls, expected_calls) last_commit = context.commits[-1] self.assertEqual(last_commit.message.title, "Merge \"foo bar commit\"") self.assertEqual(last_commit.message.body, []) self.assertEqual(last_commit.author_name, "test author") self.assertEqual(last_commit.author_email, "*****@*****.**") self.assertEqual(last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))) self.assertListEqual(last_commit.parents, ["abc", "def"]) self.assertTrue(last_commit.is_merge_commit) # assert that changed files are read using git command sh.git.assert_called_once_with('diff-tree', '--no-commit-id', '--name-only', '-r', 'HEAD', **expected_sh_special_args) self.assertListEqual(last_commit.changed_files, ["file1.txt", "path/to/file2.txt"])
def test_get_latest_commit_fixup_squash_commit(self, sh): commit_types = ["fixup", "squash"] for commit_type in commit_types: sample_sha = "d8ac47e9f2923c7f22d8668e3a1ed04eb4cdbca9" sh.git.side_effect = [sample_sha, u"test åuthor\x00test-emå[email protected]\x002016-12-03 15:28:15 01:00\x00åbc\n" u"{0}! \"foo bår commit\"".format(commit_type), u"file1.txt\npåth/to/file2.txt\n"] context = GitContext.from_local_repository(u"fåke/path") # assert that commit info was read using git command expected_calls = [ call("log", "-1", "--pretty=%H", **self.expected_sh_special_args), call("log", sample_sha, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args), call('diff-tree', '--no-commit-id', '--name-only', '-r', sample_sha, **self.expected_sh_special_args) ] self.assertEqual(sh.git.mock_calls, expected_calls) last_commit = context.commits[-1] self.assertEqual(last_commit.message.title, u"{0}! \"foo bår commit\"".format(commit_type)) self.assertEqual(last_commit.message.body, []) self.assertEqual(last_commit.author_name, u"test åuthor") self.assertEqual(last_commit.author_email, u"test-emå[email protected]") self.assertEqual(last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))) self.assertListEqual(last_commit.parents, [u"åbc"]) # Asserting that squash and fixup are correct for type in commit_types: attr = "is_" + type + "_commit" self.assertEqual(getattr(last_commit, attr), commit_type == type) self.assertFalse(last_commit.is_merge_commit) self.assertListEqual(last_commit.changed_files, ["file1.txt", u"påth/to/file2.txt"]) sh.git.reset_mock()
def test_get_latest_commit(self, sh): sample_sha = "d8ac47e9f2923c7f22d8668e3a1ed04eb4cdbca9" sh.git.side_effect = [ sample_sha, u"test åuthor,test-emå[email protected],2016-12-03 15:28:15 01:00,åbc\n" u"cömmit-title\n\ncömmit-body", u"file1.txt\npåth/to/file2.txt\n" ] context = GitContext.from_local_repository(u"fåke/path") # assert that commit info was read using git command expected_calls = [ call("log", "-1", "--pretty=%H", **self.expected_sh_special_args), call("log", sample_sha, "-1", "--pretty=%aN,%aE,%ai,%P%n%B", **self.expected_sh_special_args), call('diff-tree', '--no-commit-id', '--name-only', '-r', sample_sha, **self.expected_sh_special_args) ] self.assertListEqual(sh.git.mock_calls, expected_calls) last_commit = context.commits[-1] self.assertEqual(last_commit.message.title, u"cömmit-title") self.assertEqual(last_commit.message.body, ["", u"cömmit-body"]) self.assertEqual(last_commit.author_name, u"test åuthor") self.assertEqual(last_commit.author_email, u"test-emå[email protected]") self.assertEqual( last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))) self.assertListEqual(last_commit.parents, [u"åbc"]) self.assertFalse(last_commit.is_merge_commit) self.assertListEqual(last_commit.changed_files, ["file1.txt", u"påth/to/file2.txt"])
def test_gitcommit_equality(self, git): # git will be called to setup the context (commentchar and current_branch), just return the same value # This only matters to test gitcontext equality, not gitcommit equality git.return_value = "foöbar" # Test simple equality case now = datetime.datetime.utcnow() context1 = GitContext() commit_message1 = GitCommitMessage(context1, "tëst\n\nfoo", "tëst\n\nfoo", "tēst", ["", "föo"]) commit1 = GitCommit(context1, commit_message1, "shä", now, "Jöhn Smith", "jö[email protected]", None, ["föo/bar"], ["brånch1", "brånch2"]) context1.commits = [commit1] context2 = GitContext() commit_message2 = GitCommitMessage(context2, "tëst\n\nfoo", "tëst\n\nfoo", "tēst", ["", "föo"]) commit2 = GitCommit(context2, commit_message1, "shä", now, "Jöhn Smith", "jö[email protected]", None, ["föo/bar"], ["brånch1", "brånch2"]) context2.commits = [commit2] self.assertEqual(context1, context2) self.assertEqual(commit_message1, commit_message2) self.assertEqual(commit1, commit2) # Check that objects are unequal when changing a single attribute kwargs = {'message': commit1.message, 'sha': commit1.sha, 'date': commit1.date, 'author_name': commit1.author_name, 'author_email': commit1.author_email, 'parents': commit1.parents, 'changed_files': commit1.changed_files, 'branches': commit1.branches} self.object_equality_test(commit1, kwargs.keys(), {"context": commit1.context}) # Check that the is_* attributes that are affected by the commit message affect equality special_messages = {'is_merge_commit': "Merge: foöbar", 'is_fixup_commit': "fixup! foöbar", 'is_squash_commit': "squash! foöbar", 'is_revert_commit': "Revert: foöbar"} for key in special_messages: kwargs_copy = copy.deepcopy(kwargs) clone1 = GitCommit(context=commit1.context, **kwargs_copy) clone1.message = GitCommitMessage.from_full_message(context1, special_messages[key]) self.assertTrue(getattr(clone1, key)) clone2 = GitCommit(context=commit1.context, **kwargs_copy) clone2.message = GitCommitMessage.from_full_message(context1, "foöbar") self.assertNotEqual(clone1, clone2)
def test_gitcontext_equality(self, sh): sh.git.side_effect = [ u"û\n", # context1: git config --get core.commentchar u"û\n", # context2: git config --get core.commentchar u"my-brånch\n", # context1: git rev-parse --abbrev-ref HEAD u"my-brånch\n", # context2: git rev-parse --abbrev-ref HEAD ] context1 = GitContext(u"fåke/path") context1.commits = [ u"fōo", u"bår" ] # we don't need real commits to check for equality context2 = GitContext(u"fåke/path") context2.commits = [u"fōo", u"bår"] self.assertEqual(context1, context2) # INEQUALITY # Different commits context2.commits = [u"hür", u"dür"] self.assertNotEqual(context1, context2) # Different repository_path context2.commits = context1.commits context2.repository_path = u"ōther/path" self.assertNotEqual(context1, context2) # Different comment_char context3 = GitContext(u"fåke/path") context3.commits = [u"fōo", u"bår"] sh.git.side_effect = ([ u"ç\n", # context3: git config --get core.commentchar u"my-brånch\n" # context3: git rev-parse --abbrev-ref HEAD ]) self.assertNotEqual(context1, context3) # Different current_branch context4 = GitContext(u"fåke/path") context4.commits = [u"fōo", u"bår"] sh.git.side_effect = ([ u"û\n", # context4: git config --get core.commentchar u"different-brånch\n" # context4: git rev-parse --abbrev-ref HEAD ]) self.assertNotEqual(context1, context4)
def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj[0] msg_filename = ctx.obj[3] # Let's determine where our input data is coming from: # Order of precedence: # 1. Any data specified via --msg-filename # 2. Any data sent to stdin # 3. Fallback to reading from local repository stdin_input = get_stdin_data() if msg_filename: LOG.debug("Attempting to read from --msg-filename.") gitcontext = GitContext.from_commit_msg(ustr(msg_filename.read())) elif stdin_input: LOG.debug("No --msg-filename flag. Attempting to read from stdin.") gitcontext = GitContext.from_commit_msg(stdin_input) else: LOG.debug( "No --msg-filename flag, no or empty data passed to stdin. Attempting to read from the local repo." ) gitcontext = GitContext.from_local_repository(lint_config.target, ctx.obj[2]) number_of_commits = len(gitcontext.commits) # Exit if we don't have commits in the specified range. Use a 0 exit code, since a popular use-case is one # where users are using --commits in a check job to check the commit messages inside a CI job. By returning 0, we # ensure that these jobs don't fail if for whatever reason the specified commit range is empty. if number_of_commits == 0: LOG.debug(u'No commits in range "%s"', ctx.obj[2]) ctx.exit(0) general_config_builder = ctx.obj[1] last_commit = gitcontext.commits[-1] # Let's get linting! first_violation = True exit_code = 0 for commit in gitcontext.commits: # Build a config_builder taking into account the commit specific config (if any) config_builder = general_config_builder.clone() config_builder.set_config_from_commit(commit) # Create a deepcopy from the original config, so we have a unique config object per commit # This is important for configuration rules to be able to modifying the config on a per commit basis commit_config = config_builder.build(copy.deepcopy(lint_config)) # Actually do the linting linter = GitLinter(commit_config) violations = linter.lint(commit) # exit code equals the total number of violations in all commits exit_code += len(violations) if violations: # Display the commit hash & new lines intelligently if number_of_commits > 1 and commit.sha: linter.display.e(u"{0}Commit {1}:".format( "\n" if not first_violation or commit is last_commit else "", commit.sha[:10])) linter.print_violations(violations) first_violation = False # cap actual max exit code because bash doesn't like exit codes larger than 255: # http://tldp.org/LDP/abs/html/exitcodes.html exit_code = min(MAX_VIOLATION_ERROR_CODE, exit_code) LOG.debug("Exit Code = %s", exit_code) ctx.exit(exit_code)
def test_gitcommitmessage_equality(self): commit_message1 = GitCommitMessage(GitContext(), "tëst\n\nfoo", "tëst\n\nfoo", "tēst", ["", "föo"]) attrs = ['original', 'full', 'title', 'body'] self.object_equality_test(commit_message1, attrs, {"context": commit_message1.context})
def test_get_latest_commit_fixup_squash_commit(self, sh): commit_types = ["fixup", "squash"] for commit_type in commit_types: sample_sha = "d8ac47e9f2923c7f22d8668e3a1ed04eb4cdbca9" sh.git.side_effect = [ sample_sha, u"test åuthor\x00test-emå[email protected]\x002016-12-03 15:28:15 +0100\x00åbc\n" u"{0}! \"foo bår commit\"".format(commit_type), u"#", # git config --get core.commentchar u"file1.txt\npåth/to/file2.txt\n", u"foöbar\n* hürdur\n" ] context = GitContext.from_local_repository(u"fåke/path") # assert that commit info was read using git command expected_calls = [ call("log", "-1", "--pretty=%H", **self.expected_sh_special_args), call("log", sample_sha, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args), call('config', '--get', 'core.commentchar', _ok_code=[0, 1], **self.expected_sh_special_args), call('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', sample_sha, **self.expected_sh_special_args), call('branch', '--contains', sample_sha, **self.expected_sh_special_args) ] # Only first 'git log' call should've happened at this point self.assertEqual(sh.git.mock_calls, expected_calls[:-4]) last_commit = context.commits[-1] self.assertIsInstance(last_commit, LocalGitCommit) self.assertEqual(last_commit.sha, sample_sha) self.assertEqual(last_commit.message.title, u"{0}! \"foo bår commit\"".format(commit_type)) self.assertEqual(last_commit.message.body, []) self.assertEqual(last_commit.author_name, u"test åuthor") self.assertEqual(last_commit.author_email, u"test-emå[email protected]") self.assertEqual( last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))) self.assertListEqual(last_commit.parents, [u"åbc"]) # First 2 'git log' calls should've happened at this point self.assertEqual(sh.git.mock_calls, expected_calls[:3]) # Asserting that squash and fixup are correct for type in commit_types: attr = "is_" + type + "_commit" self.assertEqual(getattr(last_commit, attr), commit_type == type) self.assertFalse(last_commit.is_merge_commit) self.assertFalse(last_commit.is_revert_commit) self.assertListEqual(last_commit.changed_files, ["file1.txt", u"påth/to/file2.txt"]) self.assertListEqual(last_commit.changed_files, ["file1.txt", u"påth/to/file2.txt"]) # 'git diff-tree' should have happened at this point self.assertListEqual(sh.git.mock_calls, expected_calls[:4]) self.assertListEqual(last_commit.branches, [u"foöbar", u"hürdur"]) # All expected calls should've happened at this point self.assertListEqual(sh.git.mock_calls, expected_calls) sh.git.reset_mock()
def test_staged_commit(self, now, sh): # StagedLocalGitCommit() sh.git.side_effect = [ u"#", # git config --get core.commentchar u"test åuthor\n", # git config --get user.name u"test-emå[email protected]\n", # git config --get user.email u"my-brånch\n", # git rev-parse --abbrev-ref HEAD u"file1.txt\npåth/to/file2.txt\n", ] now.side_effect = [arrow.get("2020-02-19T12:18:46.675182+01:00")] # We use a fixup commit, just to test a non-default path context = GitContext.from_staged_commit( u"fixup! Foōbar 123\n\ncömmit-body\n", u"fåke/path") # git calls we're expexting expected_calls = [ call('config', '--get', 'core.commentchar', _ok_code=[0, 1], **self.expected_sh_special_args), call('config', '--get', 'user.name', **self.expected_sh_special_args), call('config', '--get', 'user.email', **self.expected_sh_special_args), call("rev-parse", "--abbrev-ref", "HEAD", **self.expected_sh_special_args), call("diff", "--staged", "--name-only", "-r", **self.expected_sh_special_args) ] last_commit = context.commits[-1] self.assertIsInstance(last_commit, StagedLocalGitCommit) self.assertIsNone(last_commit.sha, None) self.assertEqual(last_commit.message.title, u"fixup! Foōbar 123") self.assertEqual(last_commit.message.body, ["", u"cömmit-body"]) # Only `git config --get core.commentchar` should've happened up until this point self.assertListEqual(sh.git.mock_calls, expected_calls[0:1]) self.assertEqual(last_commit.author_name, u"test åuthor") self.assertListEqual(sh.git.mock_calls, expected_calls[0:2]) self.assertEqual(last_commit.author_email, u"test-emå[email protected]") self.assertListEqual(sh.git.mock_calls, expected_calls[0:3]) self.assertEqual( last_commit.date, datetime.datetime(2020, 2, 19, 12, 18, 46, tzinfo=dateutil.tz.tzoffset("+0100", 3600))) now.assert_called_once() self.assertListEqual(last_commit.parents, []) self.assertFalse(last_commit.is_merge_commit) self.assertTrue(last_commit.is_fixup_commit) self.assertFalse(last_commit.is_squash_commit) self.assertFalse(last_commit.is_revert_commit) self.assertListEqual(last_commit.branches, [u"my-brånch"]) self.assertListEqual(sh.git.mock_calls, expected_calls[0:4]) self.assertListEqual(last_commit.changed_files, ["file1.txt", u"påth/to/file2.txt"]) self.assertListEqual(sh.git.mock_calls, expected_calls[0:5])
def test_from_local_repository_specific_ref(self, sh): sample_sha = "myspecialref" sh.git.side_effect = [ sample_sha, u"test åuthor\x00test-emå[email protected]\x002016-12-03 15:28:15 +0100\x00åbc\n" u"cömmit-title\n\ncömmit-body", u"#", # git config --get core.commentchar u"file1.txt\npåth/to/file2.txt\n", u"foöbar\n* hürdur\n" ] context = GitContext.from_local_repository(u"fåke/path", sample_sha) # assert that commit info was read using git command expected_calls = [ call("rev-list", sample_sha, **self.expected_sh_special_args), call("log", sample_sha, "-1", "--pretty=%aN%x00%aE%x00%ai%x00%P%n%B", **self.expected_sh_special_args), call('config', '--get', 'core.commentchar', _ok_code=[0, 1], **self.expected_sh_special_args), call('diff-tree', '--no-commit-id', '--name-only', '-r', '--root', sample_sha, **self.expected_sh_special_args), call('branch', '--contains', sample_sha, **self.expected_sh_special_args) ] # Only first 'git log' call should've happened at this point self.assertEqual(sh.git.mock_calls, expected_calls[:1]) last_commit = context.commits[-1] self.assertIsInstance(last_commit, LocalGitCommit) self.assertEqual(last_commit.sha, sample_sha) self.assertEqual(last_commit.message.title, u"cömmit-title") self.assertEqual(last_commit.message.body, ["", u"cömmit-body"]) self.assertEqual(last_commit.author_name, u"test åuthor") self.assertEqual(last_commit.author_email, u"test-emå[email protected]") self.assertEqual( last_commit.date, datetime.datetime(2016, 12, 3, 15, 28, 15, tzinfo=dateutil.tz.tzoffset("+0100", 3600))) self.assertListEqual(last_commit.parents, [u"åbc"]) self.assertFalse(last_commit.is_merge_commit) self.assertFalse(last_commit.is_fixup_commit) self.assertFalse(last_commit.is_squash_commit) self.assertFalse(last_commit.is_revert_commit) # First 2 'git log' calls should've happened at this point self.assertListEqual(sh.git.mock_calls, expected_calls[:3]) self.assertListEqual(last_commit.changed_files, ["file1.txt", u"påth/to/file2.txt"]) # 'git diff-tree' should have happened at this point self.assertListEqual(sh.git.mock_calls, expected_calls[:4]) self.assertListEqual(last_commit.branches, [u"foöbar", u"hürdur"]) # All expected calls should've happened at this point self.assertListEqual(sh.git.mock_calls, expected_calls)