Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GitCommitBear.py: Detect revert commits #2241

Closed
wants to merge 1 commit into from
Closed

Conversation

kriti21
Copy link
Contributor

@kriti21 kriti21 commented Jan 17, 2018

This detects revert commits
and checks they have a reason as well.

Closes #337

Checklist

  • I read the commit guidelines and I've followed
    them.
  • I ran coala over my code locally. (All commits have to pass
    individually.
    It is not sufficient to have "fixup commits" on your PR,
    our bot will still report the issues for the previous commit.) You will
    likely receive a lot of bot comments and build failures if coala does not
    pass on every single commit!

Copy link
Contributor

@newbazz newbazz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to add tests.

@@ -123,6 +123,9 @@ def run(self, allow_empty_commit_message: bool = False, **kwargs):
yield Result(self, 'HEAD commit has no message.')
return

if shortlog.startswith('Revert') and len(body) == 0:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you are not detecting revert commits if they do have a reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think revert commits with reason have any problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When revert commits don't have any reason then they are wrong. Hence, detected only then.

Copy link

@ishanSrt ishanSrt Jan 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what needs to be done here. On what was meant by the issue, whether you have to detect revert commits and an additional check if they don't have a reason, or for only when they don't have a reason. You should discuss this with someone cc @sils

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did mention @sils once on gitter asking something like this. Never got any response.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you need to change tests again accordingly, so its better to ask someone what to do first ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t particularly like this solution since reason could be given in the shortlog as well, and failing coala on a repository due to that is not good.
Also, is there a better way to detect reverting of commits? I couldn’t find one myself, but it seems this might over-trigger quite easily.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also say: Simple by default, powerful if needed.
We should implement other settings controlling what is considered a revert commit. But that can be part of a new issue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I would remove the len(body) part

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And btw: I want to see a setting to enable looking for revert-commits 👍

@kriti21 kriti21 force-pushed the vcsbear branch 6 times, most recently from 480d390 to b4e0f48 Compare January 19, 2018 12:30
@Makman2
Copy link
Member

Makman2 commented Jan 21, 2018

Commit message shortlog: Please no space before colon ;)

@Makman2
Copy link
Member

Makman2 commented Jan 21, 2018

unack b4e0f48

@Makman2
Copy link
Member

Makman2 commented Feb 13, 2018

Comments not all addressed :)

@Makman2
Copy link
Member

Makman2 commented Feb 13, 2018

unack 0105d2b

@kriti21 kriti21 force-pushed the vcsbear branch 4 times, most recently from a80f473 to 28aa030 Compare February 16, 2018 17:53
@@ -123,6 +127,9 @@ def run(self, allow_empty_commit_message: bool = False, **kwargs):
yield Result(self, 'HEAD commit has no message.')
return

if shortlog.startswith('Revert') and check_revert_commits:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For performance reasons, switch both conditions:

if check_revert_commits and shortlog.startswith('Revert'):

@@ -96,7 +96,9 @@ def get_host_from_remotes():
netloc = urlparse(url)[1]
return netloc.split('.')[0]

def run(self, allow_empty_commit_message: bool = False, **kwargs):
def run(self, allow_empty_commit_message: bool = False,
check_revert_commits: bool = True,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe to fit the naming scheme: allow_revert_commits? And it's by default False? (tbh I'm not sure what should be default for this anyway...)

@@ -105,6 +107,8 @@ def run(self, allow_empty_commit_message: bool = False, **kwargs):

:param allow_empty_commit_message: Whether empty commit messages are
allowed or not.
:param check_revert_commits: whether revert commits are to be
checked or not.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Bad docstring indentation.
  2. First letter should be upper-case.

@@ -125,6 +125,14 @@ def test_empty_message(self):
[])
self.assert_no_msgs()

def test_revert_commit(self):
self.git_commit('Revert : identifies revert commit\n')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, you can maybe try to invoke git revert itself. That way we can see if we are really compliant with git's revert commits. So commit some arbitrary stuff, and then revert and test.

@Makman2
Copy link
Member

Makman2 commented Feb 18, 2018

unack 28aa030

@@ -125,6 +127,45 @@ def test_empty_message(self):
[])
self.assert_no_msgs()

def test_revert_checks(self):
self.run_git_command('commit', stdin='Test commit')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use self.run_git_command for committing only after doing a git add.
Otherwise this will fail due to empty diffs and as a result no commit would be made.
And a git rev-parse HEAD on a repository with no commit will result in failures.

Consider using self.git_commit instead as it allows empty commits and can be done even without doing a git add

Rather if you want to use run_git_command, use it to generate a revert commit
For e.g self.run_git_command('revert', 'head')

headline_regex = re.compile(r'^This reverts commit [0-9a-f]{40}')
sha_regex = re.compile(r'\b[0-9a-f]{40}\b')

if (not allow_revert_commits_without_reason and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question:
What will happen in case the user allows revert commits without reason and while generating a revert commit, say the shortlog doesn't match or the commit sha is altered for some reason ? (via a git commit --amend for example)

@kriti21
Copy link
Contributor Author

kriti21 commented May 6, 2018

Please review @jayvdb @virresh

'This is body of test commit1\n')
commit_hash, _ = run_shell_command('git rev-parse HEAD')

self.run_git_command('revert', 'head')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These revert commits are redundant since you are already making another commit on top of these, so Commit Bear is never run on them
(Similarly on line 179)

commit_hash, _ = run_shell_command('git rev-parse HEAD')
self.run_git_command('revert', 'head')

self.git_commit('Revert "Test commit3"\n\n'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the default format of a revert commit, you can omit creating this one manually and just use the commit generated in line 155


if revert_shortlog_regex.match(shortlog):
reverted_shortlog = shortlog.lstrip('Revert ')
reverted_shortlog = reverted_shortlog.strip('"')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider having a look at capturing and non-capturing regexes

commit_sha = re.search(HEADLINE_REGEX, headline).group(2)
command = ('git log -n 1 ' +
str(commit_sha) + ' --pretty=%B')
rstdout, _ = run_shell_command(command)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question : What would happen if the commit_sha is an invalid number (or a commit_hash that doesn't exist) ?
Refer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does the r in rstdout stand for?

use long self-explanatory variable names.

@@ -112,8 +119,7 @@ def get_host_from_remotes():
netloc = parsed_url.host
return netloc.split('.')[0]

def run(self,
allow_empty_commit_message: bool = False,
def run(self, allow_empty_commit_message: bool = False,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this being modified?

@@ -151,6 +161,47 @@ def run(self,
body,
**self.get_issue_checks_metadata().filter_parameters(kwargs))

def check_revert(self, shortlog, body,
allow_revert_commits_without_reason: bool = False,):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a trailing comma is silly if the ) follows it.
You need to understand why you should use trailing commas.

"""
Validates revert commits.

:param shortlog: The shortlog message string.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these descriptions are laid out very messily.

REVERT_REGEX = re.compile(r'(Revert ")([\S+\s*]*)("$)')
HEADLINE_REGEX = re.compile(r'(^This reverts commit )([0-9a-f]{40})')

if REVERT_REGEX.match(shortlog):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use

if not REVERT_REGEX.match(shortlog):
    return

:param allow_revert_commits_without_reason: Whether reason is
needed for revert commits.
"""
REVERT_REGEX = re.compile(r'(Revert ")([\S+\s*]*)("$)')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this regex creates useless capture groups, making it harder to read than it needs to be.

Also [\S+\s*]* is invalid semantically.

Furthermore, there is no need to use a regex for this.

needed for revert commits.
"""
REVERT_REGEX = re.compile(r'(Revert ")([\S+\s*]*)("$)')
HEADLINE_REGEX = re.compile(r'(^This reverts commit )([0-9a-f]{40})')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this is using capture groups which are not used.

commit_sha = re.search(HEADLINE_REGEX, headline).group(2)
command = ('git log -n 1 ' +
str(commit_sha) + ' --pretty=%B')
rstdout, _ = run_shell_command(command)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does the r in rstdout stand for?

use long self-explanatory variable names.

This detects revert commits
and checks they have a reason as well.

Closes coala#337
@kriti21
Copy link
Contributor Author

kriti21 commented May 6, 2018

Please review @jayvdb @virresh @nalinbhardwaj @ishanSrt @newbazz

self.run_uut(),
['Shortlog of revert commit does not match the original commit.'])
self.assert_no_msgs()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kriti21
It would be great to have a test such as

        _,__ = run_shell_command('echo "a" >> testfile.txt ')
        self.run_git_command('add', 'testfile.txt')
        self.run_git_command('commit', '-m', '"a commit"')
        commit_hash, _ = run_shell_command('git rev-parse HEAD')
        self.run_git_command('revert', 'HEAD', '--no-edit')
        self.assertEqual(self.run_uut(), 
                         ['Revert commit does not have a reason.'])

"<Merged signature of 'run', 'check_shortlog', 'check_body'"
", 'check_issue_reference'>")
"<Merged signature of 'run', 'check_revert', 'check_shortlog'"
", 'check_body', 'check_issue_reference'>")
Copy link
Member

@damngamerz damngamerz May 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please don't change existing test unless there's a reason. I don't see any such change in your patch which might affect this test.
Maybe write a new separate test for current change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay it all cool 👍 you just added check_revert

self.git_commit('Test commit')
commit_hash, _ = run_shell_command('git rev-parse HEAD')
self.git_commit('Revert "Test commit"\n\n'
'Abcdef blablabla commit ' + str(commit_hash) + '\n\n'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test content should be self explanatory about its existence.
Please use proper sentence in place of
Abcdef blablabla commit :P

command)

if previous_commit_stderr:
yield Result(self, 'Invalid sha for reverted commit.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sha -> SHA


shortlog_start_index = shortlog.find('"')
shortlog_end_index = shortlog.rfind('"')
reverted_shortlog = shortlog[shortlog_start_index +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shortlog_start_index, shortlog_end_index and reverted_shortlog are confusing names in this context as, in other places of the code shortlog refers to the entire string including the subject and the message.

old_shortlog = previous_commit_stdout[:position] if position != - \
1 else previous_commit_stdout

if old_shortlog != reverted_shortlog:
Copy link
Member

@ksdme ksdme May 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a problem if shortlog for the previous commit barely fit within the length limit as, to be a valid revert commit, it'll have to be rewritten with extra characters in the new commit and thereby cause an impossible situation.

I think the solution would be to rethink about the shortlog structure you require for a valid revert commit. I propose it should be written as

08b54a: Revert Commit

This reason to revert would be...

Reference if any

and this structure is also more aligned with the general guidelines.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format followed is one that is generated by git itself for revert commits. This PR only checks if a reason for revert is mentioned or not. See link
I don't think changing shortlog structure would be most apt solution. One possible solution could be to increase shorlog length limit for revert commits (to maybe 60). @ksdme @jayvdb @damngamerz

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One possible solution could be to increase shorlog length limit for revert commits

Yes, whenever a revert commit is detected, and revert commits have been permitted in the repo, other rules which might reject the commit must be turned off.

yield Result(self, 'Invalid revert commit.')
return

sha_regex = re.compile(r'([0-9a-f]{40})')
Copy link
Member

@ksdme ksdme May 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the intent is also to maintain readability why not support short SHA1 hashes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git revert doesnt do that

if not shortlog.startswith('Revert '):
return

shortlog_start_index = shortlog.find('"')
Copy link
Member

@ksdme ksdme May 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't I use single quotes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git revert doesnt do that?

Copy link
Member

@ksdme ksdme May 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is about the requirement of the shortlog of the revert commit specifically to be written as Revert "Another Test commit2". I don't see any reason why using single quotes should cause a failure.


previous_commit_stdout = previous_commit_stdout.rstrip('\n')
position = previous_commit_stdout.find('\n')
old_shortlog = previous_commit_stdout[:position] if position != - \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this linebreak looks pretty confusing try breaking before the if

@jayvdb
Copy link
Member

jayvdb commented May 9, 2018

Lots of manual conflict resolution needed now that #2310 has landed.

'This is body of test commit1\n')
commit_hash, _ = run_shell_command('git rev-parse HEAD')

self.git_commit('Revert "Another Test commit2"\n\n'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test method contains many testcases of revert features. I think it will be better if we can split this into several test method like test_revert_check_success, test_revert_check_commit_no_reason, test_revert_check_invalid_sha_commit, etc

@jayvdb
Copy link
Member

jayvdb commented Aug 10, 2018

Duplicate of #2652

@jayvdb jayvdb marked this as a duplicate of #2652 Aug 10, 2018
@jayvdb jayvdb closed this Aug 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

GitCommitBear: Detect revert commits