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
28 changes: 20 additions & 8 deletions apply_pr/fabfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

from collections import OrderedDict

from apply_pr.patch_utils import filter_patch_paths
from apply_pr.patch_utils import append_pr_url, filter_patch_paths
from apply_pr.exceptions import ApplyError
from apply_pr.console import (
as_text, console_message, log_error, print_message as _print_message,
Expand Down Expand Up @@ -483,7 +483,8 @@ def find_from_to_commits(pr_number, owner='gisce', repository='erp'):

@task
def export_patches_from_git(
from_commit, to_commit, pr_number, skip_directory_pattern=None
from_commit, to_commit, pr_number, owner='gisce', repository='erp',
skip_directory_pattern=None
):
logger.info('Exporting patches from %s to %s' % (from_commit, to_commit))
deploy_path = "deploy/patches/{}".format(pr_number)
Expand All @@ -497,16 +498,21 @@ def export_patches_from_git(
local("git format-patch -o deploy/patches/%s %s..%s" % (
pr_number, from_commit, to_commit)
)
if skip_directory_pattern:
for patch_path in glob.glob(os.path.join(deploy_path, '*.patch')):
with io.open(patch_path, 'r', encoding='utf-8') as patch_file:
content = patch_file.read()
pr_url = 'https://github.com/{}/{}/pull/{}'.format(
owner, repository, pr_number
)
for patch_path in glob.glob(os.path.join(deploy_path, '*.patch')):
with io.open(patch_path, 'r', encoding='utf-8') as patch_file:
content = patch_file.read()
if skip_directory_pattern:
filtered = filter_patch_paths(content, skip_directory_pattern)
if 'diff --git ' not in filtered:
os.unlink(patch_path)
continue
with io.open(patch_path, 'w', encoding='utf-8') as patch_file:
patch_file.write(filtered)
content = filtered
content = append_pr_url(content, pr_url)
with io.open(patch_path, 'w', encoding='utf-8') as patch_file:
patch_file.write(content)


@task
Expand Down Expand Up @@ -642,6 +648,12 @@ def export_patches_from_github(
if 'diff --git ' not in content:
logger.info('Skipping empty patch %s after path filtering.', filename)
continue
content = append_pr_url(
content,
'https://github.com/{}/{}/pull/{}'.format(
owner, repository, pr_number
),
)
with open(os.path.join(patch_folder, filename), 'wb') as patch:
logger.info('Exporting patch %s.' % filename)
patch.write(content.encode('utf-8'))
Expand Down
12 changes: 12 additions & 0 deletions apply_pr/patch_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,15 @@ def filter_patch_paths(content, skip_directory_pattern=None):
if not any(pattern.search(path) for path in _diff_paths(header)):
kept.append(section)
return ''.join(kept)


def append_pr_url(content, pr_url):
"""Append the pull request URL to the commit message in a mail patch."""
diff_start = content.find('\ndiff --git ')
if diff_start == -1:
return content
separator = content.find('\n---\n')
if separator == -1 or separator > diff_start:
return content
message = content[:separator].rstrip('\n')
return '{}\n\n{}{}'.format(message, pr_url, content[separator:])
92 changes: 92 additions & 0 deletions tests/test_patch_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import os
import shutil
import subprocess
import tempfile
import unittest

from apply_pr.patch_utils import append_pr_url


PATCH = """From abc Mon Sep 17 00:00:00 2001
From: Developer <developer@example.com>
Date: Tue, 6 Oct 2026 12:00:00 +0200
Subject: [PATCH] Preserve the original title

First paragraph of the body.

Second paragraph of the commit message.
---
file.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/file.txt b/file.txt
index e69de29..7898192 100644
--- a/file.txt
+++ b/file.txt
@@ -0,0 +1 @@
+content
--
2.39.5
"""


class AppendPrUrlTest(unittest.TestCase):
def test_appends_url_after_multiline_message(self):
url = 'https://github.com/gisce/erp/pull/12345'

result = append_pr_url(PATCH, url)

self.assertIn(
'Second paragraph of the commit message.\n\n{}\n---\n'
' file.txt | 1 +'.format(url),
result,
)
self.assertEqual(result.count(url), 1)
self.assertIn('Subject: [PATCH] Preserve the original title', result)

def test_git_am_creates_commit_with_original_message_and_url(self):
checkout = tempfile.mkdtemp(prefix='apply-pr-message-test-')
self.addCleanup(shutil.rmtree, checkout)
subprocess.check_call(['git', 'init', '-q'], cwd=checkout)
subprocess.check_call(
['git', 'config', 'user.name', 'Sastre Test'], cwd=checkout
)
subprocess.check_call(
['git', 'config', 'user.email', 'sastre@example.net'], cwd=checkout
)
open(os.path.join(checkout, 'file.txt'), 'w').close()
subprocess.check_call(['git', 'add', 'file.txt'], cwd=checkout)
subprocess.check_call(
['git', 'commit', '-q', '-m', 'Initial commit'], cwd=checkout
)
url = 'https://github.com/gisce/erp/pull/12345'
patch_path = os.path.join(checkout, 'change.patch')
with open(patch_path, 'w') as patch_file:
patch_file.write(append_pr_url(PATCH, url))

subprocess.check_call(['git', 'am', patch_path], cwd=checkout)

message = subprocess.check_output(
['git', 'log', '-1', '--format=%B'], cwd=checkout
).decode('utf-8').strip()
self.assertEqual(
message,
'Preserve the original title\n\n'
'First paragraph of the body.\n\n'
'Second paragraph of the commit message.\n\n{}'.format(url),
)

def test_leaves_a_plain_diff_unchanged(self):
diff = 'diff --git a/file.txt b/file.txt\n'

self.assertEqual(
append_pr_url(diff, 'https://github.com/gisce/erp/pull/12345'),
diff,
)


if __name__ == '__main__':
unittest.main()