-
Notifications
You must be signed in to change notification settings - Fork 280
prepare-commit-message hook #520
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
Conversation
| # exists); or commit, followed by a commit SHA-1 (if a -c, -C or --amend | ||
| # option was given) | ||
| def commit_msg_source | ||
| (@args[1] || 'commit').to_sym |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While testing locally, the args passed in a standard commit were just the filename in question (.git/COMMIT_EDITMSG), which wasn't expected as per the git docs. Do you have more context on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the documentation states that the commit arg is only included if you are amending an existing commit (via --amend) or copying a commit message from another commit (via -c or -C). Thus we should not be defining a default value here.
| @@ -0,0 +1,59 @@ | |||
| require 'spec_helper' | |||
|
|
|||
| describe Overcommit::Hook::PrepareCommitMsg::ReplaceBranch do | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file lacks repo tests. Let me know if these are required for this PR to be merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine. As long as we have rigorous tests for the base class which validate the behavior of our commit message modifier helpers and the commit_message_source / commit_message_source_ref helpers, we can stub those in hook implementation tests.
| subject { context.commit } | ||
|
|
||
| context "source isn't :commit" do | ||
| it { should == `git rev-parse HEAD` } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit ugly. Can you think of a better way to write this spec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll be able to remove this particular test given my comment above.
| def prepend_commit_message | ||
| Overcommit::Utils.log.debug("Writing #{commit_msg_filename} with #{new_template}") | ||
| old_contents = File.read(commit_msg_filename) | ||
| File.open(commit_msg_filename, 'w') do |commit_file| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this code should be extracted into a utility function in base? prepend_commit_msg seems like a common-enough occurrence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're absolutely right that we should provide a helper for modifying the commit message. The most important feature we must provide here is a mechanism for modifying a commit message atomically.
Since Overcommit parallelizes hooks, we have the risk of clobbering messages added by different hook runs (for example, if two hooks read the current commit message filename on disk at the same time, each update it to their liking, and then save, one update will be clobbered by the other).
We need to ensure we handle this case. Perhaps a block that obtains a lock, e.g.
modify_commit_message do |current_msg|
# In here, hook has exclusive access to the commit message
"My prepended message\n" + current_msg # Modified message is returned
endYou can then implement prepend_commit_message and append_commit_message using modify_commit_message (whether these provide significant value to hook authors is a separate question).
We could also ensure any modifications to the commit message are made in memory, so we don't need to save to the file until the end of the hook run.
29c6b10 to
6916078
Compare
|
Sorry about the delay here. I don't have enough context to give this a proper review, so I'm going to ask @sds to see if he does. |
sds
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @dcluna, my sincere apologies for the delay in response. I know it's frustrating to put effort into an implementation only to have a pull request go unanswered.
I have a number of minor concerns with the implementation, but the general pattern here looks good. Thanks for taking the time to write tests and for putting up an initial draft of ideas.
If my comments are addressed I will be happy to merge this feature!
config/default.yml
Outdated
| enabled: false | ||
| description: 'Prepends the commit message with text based on the branch name' | ||
| branch_pattern: '\A.*\w+[-_](\d+).*\z' | ||
| replacement_text: '[#\1] [ci skip]' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems odd to me that [ci skip] would be included by default here. Remember, we're defining a default configuration for anyone choosing to enable this hook.
Can you elaborate why the default user would want this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hook's main function is to match a ticket identifier in the beginning of the branch name, so if you follow a certain naming convention for your branches, like $BUGTRACKER_NAME-$ID_IN_BUGTRACKER-$BRANCH_DESCRIPTION, you'll have those already set up in the commit message name.
I use this for Github's JIRA integration, not sure if I should remove the [ci skip] to a hook of its own or just remove it altogether. WDYT?
config/default.yml
Outdated
| replacement_text: '[#\1] [ci skip]' | ||
| on_fail: warn | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can remove the extra line.
| # exists); or commit, followed by a commit SHA-1 (if a -c, -C or --amend | ||
| # option was given) | ||
| def commit_msg_source | ||
| (@args[1] || 'commit').to_sym |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the documentation states that the commit arg is only included if you are amending an existing commit (via --amend) or copying a commit message from another commit (via -c or -C). Thus we should not be defining a default value here.
| def prepend_commit_message | ||
| Overcommit::Utils.log.debug("Writing #{commit_msg_filename} with #{new_template}") | ||
| old_contents = File.read(commit_msg_filename) | ||
| File.open(commit_msg_filename, 'w') do |commit_file| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're absolutely right that we should provide a helper for modifying the commit message. The most important feature we must provide here is a mechanism for modifying a commit message atomically.
Since Overcommit parallelizes hooks, we have the risk of clobbering messages added by different hook runs (for example, if two hooks read the current commit message filename on disk at the same time, each update it to their liking, and then save, one update will be clobbered by the other).
We need to ensure we handle this case. Perhaps a block that obtains a lock, e.g.
modify_commit_message do |current_msg|
# In here, hook has exclusive access to the commit message
"My prepended message\n" + current_msg # Modified message is returned
endYou can then implement prepend_commit_message and append_commit_message using modify_commit_message (whether these provide significant value to hook authors is a separate question).
We could also ensure any modifications to the commit message are made in memory, so we don't need to save to the file until the end of the hook run.
| # Returns the commit's SHA-1. | ||
| # If commit_msg_source is :commit, it's passed through the command-line. | ||
| def commit | ||
| @args[2] || `git rev-parse HEAD` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to be clear about the meaning of the value returned by this helper method. From my comment above, if the commit is defined then arg[2] will be the SHA-1 of the commit we're copying, but not the commit about to be created. I think this will be confusing and we should be explicit.
Let's call this method commit_message_source_ref. While it's not a true ref I think the wording helps emphasize that it's referring to a commit hash ref, and emphasizes the connection to the commit_message_source helper above.
| @@ -0,0 +1,59 @@ | |||
| require 'spec_helper' | |||
|
|
|||
| describe Overcommit::Hook::PrepareCommitMsg::ReplaceBranch do | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine. As long as we have rigorous tests for the base class which validate the behavior of our commit message modifier helpers and the commit_message_source / commit_message_source_ref helpers, we can stub those in hook implementation tests.
| # merge or a .git/MERGE_MSG file exists); squash (if a .git/SQUASH_MSG file | ||
| # exists); or commit, followed by a commit SHA-1 (if a -c, -C or --amend | ||
| # option was given) | ||
| def commit_msg_source |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's call this commit_message_source (we aren't using the short msg form in any other consumable API).
| subject { context.commit } | ||
|
|
||
| context "source isn't :commit" do | ||
| it { should == `git rev-parse HEAD` } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll be able to remove this particular test given my comment above.
|
@dcluna Is this still active? I'd love to see prepare-commit-message support merged. I'm happy to tidy up this PR if necessary... |
|
@bpaul sorry, I haven't had enough time to tidy this up. Feel free to move this forward |
|
@bpaul Hey, are you still planning on tidying this up? I'd be very much interested in having this hook as part of overcommit |
correctly configured, automatically generates a commit message template based on the branch name. Groups captured in the branch_pattern regex can be used in replacement_text; see the accompanying spec for details. Also, replacement_text can be a path to a file, whose text will be processed following the same rules.
and default.yml config file
fac41cf to
8f2b48a
Compare
|
@sds I hope to have addressed your concerns with the latest updates. I went with a lock-based solution for the (For the sake of completeness, for an in-memory solution: I think it'd be possible to write a custom object that allowed add/delete/modify operations in-memory, but then that'd require a custom This implementation requires that all |
c959c6c to
bdf7359
Compare
bdf7359 to
ae2d529
Compare
commit_message_source API
|
Thanks @dcluna! |
As mentioned in #516, this commit implements the
prepare-commit-msghook, with a default hook that prepends a message based on the branch name through regex substitution.