Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Provide a concise overview of the issue or rationale behind the proposed changes
- [ ] This PR follows the [contribution guidelines](https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/submitting-patches/).
- [ ] This PR **does not** disclose a security vulnerability (see [vulnerability reporting](https://docs.djangoproject.com/en/stable/internals/security/)).
- [ ] This PR targets the `main` branch. <!-- Backports will be evaluated and done by mergers, when necessary. -->
- [ ] The commit message is written in past tense, mentions the ticket number, and ends with a period.
- [ ] The commit message is written in past tense, mentions the ticket number, and ends with a period (see [guidelines](https://docs.djangoproject.com/en/dev/internals/contributing/committing-code/#committing-guidelines)).
- [ ] I have checked the "Has patch" ticket flag in the Trac system.
- [ ] I have added or updated relevant tests.
- [ ] I have added or updated relevant docs, including release notes if applicable.
Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/check_commit_messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,39 @@ jobs:
fi

echo "✅ All commits have the required prefix."

check-commit-suffix:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false

- name: Fetch relevant branches
run: |
git fetch origin $BASE:base
git fetch origin pull/${{ github.event.pull_request.number }}/head:pr

- name: Check commit messages suffix
run: |
COMMITS=$(git rev-list base..pr)
echo "Checking commit messages for trailing period"
FAIL=0
for SHA in $COMMITS; do
MSG=$(git log -1 --pretty=%s $SHA)
echo "Checking commit $SHA: $MSG"
if [[ "${MSG: -1}" != "." ]]; then
echo "❌ Commit $SHA must end with a period."
echo "Refer to the guidelines linked in the PR checklist for commit message format."
echo "For how to rewrite commit messages, see https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/working-with-git/#editing-commit-messages"
FAIL=1
fi
done

if [[ $FAIL -eq 1 ]]; then
echo "One or more commit messages are missing a trailing period."
exit 1
fi

echo "✅ All commits have the required trailing period."
6 changes: 3 additions & 3 deletions django/conf/locale/eu/formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
#
# The *_FORMAT strings use the Django date format syntax,
# see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = r"Y\k\o N j\a"
DATE_FORMAT = r"Y(\e)\k\o N\k j"
TIME_FORMAT = "H:i"
DATETIME_FORMAT = r"Y\k\o N j\a, H:i"
YEAR_MONTH_FORMAT = r"Y\k\o F"
DATETIME_FORMAT = r"Y(\e)\k\o N\k j, H:i"
YEAR_MONTH_FORMAT = r"Y(\e)\k\o F"
MONTH_DAY_FORMAT = r"F\r\e\n j\a"
SHORT_DATE_FORMAT = "Y-m-d"
SHORT_DATETIME_FORMAT = "Y-m-d H:i"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,11 @@ def collect(self):
# Add a blank line before the traceback, otherwise it's
# too easy to miss the relevant part of the error message.
self.stderr.write()
raise processed
# Re-raise exceptions as CommandError and display notes.
message = str(processed)
if hasattr(processed, "__notes__"):
message += "\n" + "\n".join(processed.__notes__)
raise CommandError(message) from processed
if processed:
self.log(
"Post-processed '%s' as '%s'" % (original_path, processed_path),
Expand Down
28 changes: 22 additions & 6 deletions django/contrib/staticfiles/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,16 @@ def url_converter(self, name, hashed_files, template=None, comment_blocks=None):
if template is None:
template = self.default_template

def _line_at_position(content, position):
start = content.rfind("\n", 0, position) + 1
end = content.find("\n", position)
end = end if end != -1 else len(content)
line_num = content.count("\n", 0, start) + 1
msg = f"\n{line_num}: {content[start:end]}"
if len(msg) > 79:
return f"\n{line_num}"
return msg

def converter(matchobj):
"""
Convert the matched URL to a normalized and hashed URL.
Expand Down Expand Up @@ -276,12 +286,18 @@ def converter(matchobj):

# Determine the hashed name of the target file with the storage
# backend.
hashed_url = self._url(
self._stored_name,
unquote(target_name),
force=True,
hashed_files=hashed_files,
)
try:
hashed_url = self._url(
self._stored_name,
unquote(target_name),
force=True,
hashed_files=hashed_files,
)
except ValueError as exc:
line = _line_at_position(matchobj.string, matchobj.start())
note = f"{name!r} contains this reference {matched!r} on line {line}"
exc.add_note(note)
raise exc

transformed_url = "/".join(
url_path.split("/")[:-1] + hashed_url.split("/")[-1:]
Expand Down
9 changes: 6 additions & 3 deletions django/core/handlers/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,13 @@ def __init__(self, scope, body_file):
self.path = scope["path"]
self.script_name = get_script_prefix(scope)
if self.script_name:
# TODO: Better is-prefix checking, slash handling?
self.path_info = scope["path"].removeprefix(self.script_name)
script_name = self.script_name.rstrip("/")
if self.path.startswith(script_name + "/") or self.path == script_name:
self.path_info = self.path[len(script_name) :]
else:
self.path_info = self.path
else:
self.path_info = scope["path"]
self.path_info = self.path
# HTTP basics.
self.method = self.scope["method"].upper()
# Ensure query string is encoded correctly.
Expand Down
120 changes: 90 additions & 30 deletions docs/internals/contributing/committing-code.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ Committing code
===============

This section is addressed to the mergers and to anyone interested in knowing
how code gets committed into Django. If you're a community member who wants to
contribute code to Django, look at
how code gets committed into Django. The :ref:`committing-guidelines` apply
to all contributors, with or without commit rights.

If you're a community member who wants to contribute code to Django, look at
:doc:`/internals/contributing/writing-code/working-with-git` instead.

.. _handling-pull-requests:
Expand Down Expand Up @@ -102,8 +104,8 @@ community, getting work done, and having a usable commit history.
Committing guidelines
=====================

In addition, please follow the following guidelines when committing code to
Django's Git repository:
These guidelines apply to all commits to Django's Git repository, whether
submitted by a contributor via a pull request or landed directly by a merger:

* Never change the published history of ``django/django`` branches by force
pushing. If you absolutely must (for security reasons for example), first
Expand All @@ -119,16 +121,27 @@ Django's Git repository:
discussions immediately, so you may have to wait a couple of days before
getting a response.

* Write detailed commit messages in the past tense, not present tense.
* Write detailed commit messages in the past tense, not present tense, and
end the subject line with a period.

* Good: "Fixed Unicode bug in RSS API."
* Bad: "Fixes Unicode bug in RSS API."
* Bad: "Fixing Unicode bug in RSS API."
* Correct: "Fixed Unicode bug in RSS API."
* Incorrect: "Fixes Unicode bug in RSS API." (present tense)
* Incorrect: "Fixing Unicode bug in RSS API." ("-ing" form)
* Incorrect: "Fixed Unicode bug in RSS API" (missing trailing period)

The commit message should be in lines of 72 chars maximum. There should be
a subject line, separated by a blank line and then paragraphs of 72 char
lines. The limits are soft. For the subject line, shorter is better. In the
body of the commit message more detail is better than less:
lines. The limits are soft. For the subject line, shorter is better.

In the body of the commit message more detail is better than less, and should
explain *why* the change was made, not *what* was changed or *how*. The code
itself shows what changed; the commit message should provide the context and
reasoning that the code cannot.

Credit the contributors in the commit message: "Thanks A for the report and B
for review." Use git's `Co-Authored-By`_ as appropriate.

For example:

.. code-block:: none

Expand All @@ -138,22 +151,18 @@ Django's Git repository:
specific tasks. Added guidelines of how to use Git, GitHub, and
how to use pull request together with Trac instead.

Credit the contributors in the commit message: "Thanks A for the report and B
for review." Use git's `Co-Authored-By`_ as appropriate.
Thanks to Full Name for the report, and to Reviewer for reviews.

.. _Co-Authored-By: https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors

* For commits to a branch, prefix the commit message with the branch name.
For example: "[1.4.x] Fixed #xxxxx -- Added support for mind reading."

* Limit commits to the most granular change that makes sense. This means,
use frequent small commits rather than infrequent large commits. For
example, if implementing feature X requires a small change to library Y,
first commit the change to library Y, then commit feature X in a separate
commit. This goes a *long way* in helping everyone follow your changes.

* Separate bug fixes from feature changes. Bugfixes may need to be backported
to the stable branch, according to :ref:`supported-versions-policy`.
* Separate bug fixes from feature changes. Bugfixes may need to be
:ref:`backported <backports>`.

* If your commit closes a ticket in the Django `ticket tracker`_, begin
your commit message with the text "Fixed #xxxxx", where "xxxxx" is the
Expand All @@ -178,33 +187,84 @@ Django's Git repository:
is the number of the ticket your commit references. This will automatically
post a comment to the appropriate ticket.

* Write commit messages for backports using this pattern:
.. _backports:

.. code-block:: none
Backports
=========

[<Django version>] Fixed <ticket> -- <description>
Bug fix backports to stable branches are done exclusively by mergers, following
the :ref:`supported-versions-policy`. A backport consists of cherry-picking a
commit from ``main`` onto the target ``stable/A.B.x`` branch.

Backport of <revision> from <branch>.
A backport commit must include two things beyond the original commit message:

For example:
* A prefix ``[A.B.x]`` on the subject line, where ``A.B.x`` is the name of the
``stable/A.B.x`` branch being targeted.

.. code-block:: none
* A suffix ``Backport of <sha> from main.`` line in the commit body, pointing
to the original commit hash.

For example:

.. code-block:: none

[1.3.x] Fixed #17028 -- Changed diveintopython.org -> diveintopython.net.

Backport of 80c0cbf1c97047daed2c5b41b296bbc56fe1d7e3 from main.

There's a `script on the wiki
<https://code.djangoproject.com/wiki/MergerTips#AutomatingBackports>`_ to
automate this.

If the commit fixes a regression, include this in the commit message:
If the backport also fixes a regression, add a line identifying the commit that
introduced it:

.. code-block:: none
.. code-block:: none

Regression in 6ecccad711b52f9273b1acb07a57d3f806e93928.

(use the commit hash where the regression was introduced).
There are three ways to do a backport, depending on your workflow:

**Option 1: fully manual**

Cherry-pick the commit onto the stable branch, then amend the commit message to
add the required prefix and backport note:

.. code-block:: shell

git cherry-pick <sha>
git commit --amend

If the cherry-pick produces conflicts, resolve them, stage the changes, then
run ``git cherry-pick --continue`` before amending. This option requires no
setup but relies entirely on remembering to format the message correctly.

**Option 2: using the helper script in the repo**

The ``scripts/backport.sh`` Bash script automates the cherry-pick and rewrites
the commit message with the correct prefix and backport note. Run it from the
target stable branch:

.. code-block:: shell

bash scripts/backport.sh <sha>

This is straightforward for clean cherry-picks, but if conflicts are produced,
you will need to resolve them manually and add the prefix and backport note to
the commit message yourself.

**Option 3: using the** ``prepare-commit-msg`` **git hook**

The ``scripts/prepare_commit_msg.py`` Python script can be installed as a
``prepare-commit-msg`` git hook. It automatically adds the ``[A.B.x]`` prefix
to the subject line, appends ``Backport of <sha> from main.`` to the commit
body, and ensures the subject line ends with a period, even when the
cherry-pick produces conflicts. To install it, create an executable file
``.git/hooks/prepare-commit-msg`` containing:

.. code-block:: sh

#!/bin/sh
exec python scripts/prepare_commit_msg.py "$@"

Once installed, a plain ``git cherry-pick <sha>`` on a stable branch is
sufficient; the hook handles the message formatting in all cases.

Reverting commits
=================
Expand Down
49 changes: 42 additions & 7 deletions docs/internals/contributing/writing-code/working-with-git.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ necessary:

.. code-block:: shell

git commit -m 'Added two more tests for edge cases'
git commit -m 'Added two more tests for edge cases.'

Publishing work
---------------
Expand Down Expand Up @@ -163,11 +163,46 @@ will deal with your pull request has only two options: merge it or close it.
For this reason, it isn't useful to make a pull request until the code is ready
for merging -- or sufficiently close that a merger will finish it themselves.

.. _editing-commit-messages:

Editing commit messages
-----------------------

To change the message of the most recent commit, run:

.. code-block:: shell

git commit --amend

This opens an editor with the current commit message. Edit it, save, and close
to update the commit.

To change the message of an earlier commit, use the "reword" option in
interactive rebase. For example, to reword one of the last three commits:

.. code-block:: shell

git rebase -i HEAD~3

This opens an editor listing the three commits, each prefixed with the word
"pick". Change "pick" to "reword" (or "r") on the line of the commit you want
to change, then save and close. A new editor will open for each commit marked
as "reword", allowing you to update the message.

See :ref:`committing-guidelines` for the required commit message format.

After rewriting a commit that has already been pushed to GitHub, you will need
to force-push your branch:

.. code-block:: shell

git push --force-with-lease origin ticket_xxxxx

Rebasing branches
-----------------

In the example above, you created two commits, the "Fixed ticket_xxxxx" commit
and "Added two more tests" commit.
In the example above, you created two commits, the "Fixed #xxxxx -- ..."
commit and the "Added two more tests ..." commit.

We do not want to have the entire history of your working process in your
repository. Your commit "Added two more tests" would be unhelpful noise.
Expand Down Expand Up @@ -208,7 +243,7 @@ the changes:

.. code-block:: shell

git push -f origin ticket_xxxxx
git push --force-with-lease origin ticket_xxxxx

Note that this will rewrite history of ticket_xxxxx - if you check the commit
hashes before and after the operation at GitHub you will notice that the commit
Expand Down Expand Up @@ -264,10 +299,10 @@ of:

.. code-block:: text

Made changes asked in review by <reviewer>
Made changes asked in review by <reviewer>.

- Fixed whitespace errors in foobar
- Reworded the docstring of bar()
- Fixed whitespace errors in foobar.
- Reworded the docstring of bar().

Finally, push your work back to your GitHub repository. Since you didn't touch
the public commits during the rebase, you should not need to force-push:
Expand Down
Loading
Loading