Skip to content

Instantly share code, notes, and snippets.

@achambers
Last active January 15, 2025 09:31
Show Gist options
  • Save achambers/c22c1e253107dfc5f8eb707a72e4d8cb to your computer and use it in GitHub Desktop.
Save achambers/c22c1e253107dfc5f8eb707a72e4d8cb to your computer and use it in GitHub Desktop.
Apply PR patch to monorepo branch

This script is used to help engineers apply their PRs to the migrated monorepo if rebasing is causing issues.

The script can be used via npx like so:

npx https://gist.github.com/achambers/c22c1e253107dfc5f8eb707a72e4d8cb -s <source-branch>

Where <source-branch> is the branch name of the PR branch.

(There are 2 other optional fields but they shouldn't be needed by engineers)

The general gist of this script is to:

  1. Create a patch of your changes
  2. Rename the files paths in the patch to target the new apps/embercom/* paths
  3. Create a new branch off origin/master
  4. Apply the patch to the new branch

Engineers should then create a new PR for the new branch, closing and linking to the existing one.

#!/usr/bin/env bash
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Define colors
GREEN=$(tput setaf 2)
RED=$(tput setaf 1)
YELLOW=$(tput setaf 3)
BLUE=$(tput setaf 4)
BOLD=$(tput bold)
NC=$(tput sgr0) # No Color
# Default values
target_branch="master"
patch_file="pr.patch"
# Print usage
usage() {
echo "Usage: $0 -s <source-branch> [-t <target-branch>] [-p <patch-file>]"
echo " -s: Source branch (required)"
echo " -t: Target branch (default: master)"
echo " -p: Patch file (default: pr.patch)"
exit 1
}
# Parse command line arguments
while getopts "s:t:p:h" opt; do
case $opt in
s) source_branch="$OPTARG" ;;
t) target_branch="$OPTARG" ;;
p) patch_file="$OPTARG" ;;
h) usage ;;
?) usage ;;
esac
done
# Check if source branch was provided
if [ -z "$source_branch" ]; then
echo "${RED}Error: Source branch (-s) is required${NC}"
usage
fi
# Find the remote tracking branch for target branch
remote_master=$(git rev-parse --abbrev-ref ${target_branch}@{upstream})
if [ -z "$remote_master" ]; then
echo "${RED}Error: Could not determine remote tracking branch for $target_branch${NC}"
exit 1
fi
# Extract the remote name (e.g., "origin" from "origin/master")
remote_name=${remote_master%%/*}
if [ -z "$remote_name" ]; then
echo "${RED}Error: Could not determine remote name${NC}"
exit 1
fi
# Fetch from the remote
echo "${BLUE}Fetching from $remote_name...${NC}"
if ! git fetch "$remote_name"; then
echo "${RED}Error: Failed to fetch from $remote_name${NC}"
exit 1
fi
# Step 1: Checkout the source branch
echo "${BLUE}Checking out source branch: $source_branch${NC}"
if ! git checkout "$source_branch"; then
echo "${RED}Error: Failed to checkout branch $source_branch${NC}"
exit 1
fi
# Step 2: Create patch file using merge-base
echo "${BLUE}Creating patch file from $source_branch against merge-base with $target_branch${NC}"
if ! git format-patch $(git merge-base HEAD $remote_master)..HEAD --stdout > "$patch_file"; then
echo "${RED}Error: Failed to create patch file${NC}"
exit 1
fi
# Step 3: Run rename-paths.sh
echo "${BLUE}Running rename-paths.sh on $patch_file${NC}"
if ! $SCRIPT_DIR/rename-paths.sh "$patch_file"; then
echo "${RED}Error: Failed to process patch paths${NC}"
exit 1
fi
# Step 4: Create and checkout new branch
new_branch="monorepo-conversion/${source_branch}"
echo "${BLUE}Creating new branch $new_branch from $remote_master${NC}"
if ! git checkout -b "$new_branch" "$remote_master"; then
echo "${RED}Error: Failed to create new branch${NC}"
exit 1
fi
# Step 5: Apply the patch
echo "Applying patch..."
if git apply --reject --whitespace=fix "$patch_file" 2> /tmp/patch_error; then
echo "${GREEN}Patch applied successfully!${NC}"
rm -f "$patch_file"
else
echo "${YELLOW}Warning: There were some issues applying the patch"
echo "Check the following files for rejected hunks:"
find . -name "*.rej" -type f
echo "Original error output:"
cat /tmp/patch_error
rm -f /tmp/patch_error
echo "${NC}"
exit 1
fi
{
"name": "@intercom/apply-monorepo-patch",
"version": "1.0.0",
"description": "Script to apply patches with path conversions for monorepo",
"bin": {
"apply-monorepo-patch": "./apply-monorepo-patch.sh",
"rename-paths.sh": "./rename-paths.sh"
},
"files": [
"apply-monorepo-patch.sh",
"rename-paths.sh"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Intercom",
"license": "MIT"
}
#!/usr/bin/env bash
# Check if a filename was provided
if [ $# -ne 1 ]; then
echo "Usage: $0 <patch-file>"
exit 1
fi
patch_file="$1"
# Check if input file exists
if [ ! -f "$patch_file" ]; then
echo "Error: $patch_file not found"
exit 1
fi
# Create a temporary file
temp_file=$(mktemp)
# Process the patch file
while IFS= read -r line; do
# Replace paths that start with /app, /tests, /translations, /test-fixtures, /public, or /types
if [[ $line =~ ^([+-]{3}|diff\ --git)\ a?/(app|tests|translations|test-fixtures|public|types) ]]; then
line=${line/\/app/\/apps\/embercom\/app}
line=${line/\/tests/\/apps\/embercom\/tests}
line=${line/\/translations/\/apps\/embercom\/translations}
line=${line/\/test-fixtures/\/apps\/embercom\/test-fixtures}
line=${line/\/public/\/apps\/embercom\/public}
line=${line/\/types/\/apps\/embercom\/types}
elif [[ $line =~ ^(index|---|\+\+\+)\ .*\/(app|tests|translations|test-fixtures|public|types) ]]; then
line=${line/\/app/\/apps\/embercom\/app}
line=${line/\/tests/\/apps\/embercom\/tests}
line=${line/\/translations/\/apps\/embercom\/translations}
line=${line/\/test-fixtures/\/apps\/embercom\/test-fixtures}
line=${line/\/public/\/apps\/embercom\/public}
line=${line/\/types/\/apps\/embercom\/types}
fi
echo "$line" >> "$temp_file"
done < "$patch_file"
# Replace original file with modified content
mv "$temp_file" "$patch_file"

Comments are disabled for this gist.