
Your First Pull and Merge Request With a Sprinkle of Automation
- Alex Morgan
- Engineering
- November 1, 2024
New to Git? I wrote a version of this out for myself a good while back when starting out, it helped me to keep track of these basic parts in order. I do enjoy letting full commands slip from my working memory all the time, so this kind of thing and writing your own can help.
Although there are roughly 1 million guides out there I’ll add another, walking through the first pull request from start to finish, plus there’s the added extra of an automated cleanup script at the end to keep things tidy.
What to expect:
- How to start with a new repo and your first pull request
- Basic Git commands you’ll use a lot
- Best practices for naming and organising work
- Quick clean up after your PR is merged
- Bonus: A script to automate the cleanup process
Each company or project will have a slightly different conventions so find out what the norm is for your team. If there isn’t one, guess what… collaborate and create a conventions doc for your team! This is created as a simple guide for someone new to Git (and as a quick reference too!) with a bit of a focus on Github but it’s all easily exchangeable.
Quick reference commands
Before we dive into the details, here are the commands you’ll use most often:
Daily development
# Check status
git status
# Stage and commit
git add .
git commit -m "type: your commit message"
# Push changes
git push -u origin team/yourname/feature-name
Common patterns
Branch naming
team/yourname/feature-description
feature/ticket-number/description
bugfix/issue-number/description
Commit message types
feat: New feature
fix: Bug fix
docs: Documentation changes
style: Formatting, missing semicolons, etc.
refactor: Code restructuring
test: Adding tests
chore: Maintenance tasks
The complete workflow
Clone the repository
Clone the repo using the command line, mkdir
a folder (e.g. /Projects/work/acme
) you can then run git clone
here or directly within your IDE. I use VS Code (sorry NeoVim mains) so I’ll add in some specific callouts for that.
cd ~/Projects/Work/YourCompanyName/
git clone https://github.com/YourCompany/repo-name.git
cd repo-name
To clone in VS Code:
- Open VS Code
- Press Ctrl+Shift+P (or Cmd+Shift+P on Mac) to open the command palette
- Type “Git: Clone” and select it
- Paste the repository URL and choose a local directory
Create a new branch
Follow the team’s naming convention, create a new branch for this change. Use the command line in VS Code’s integrated terminal or iTerm/terminal/warp, or whatever you use:
git checkout -b team/yourname/readme-command-update
Stage and commit the changes
You can do this in VS Code’s Source Control tab or use the command line:
git add README.md
git commit -m "docs: correct README command error"
🤓 Write great commit messages! Conventional commits can be a nice standard to follow for commit messages.
Push the branch to GitHub (or wherever)
git push -u origin solutions/yourname/readme-update
The -u
flag sets up a tracking relationship between your local branch and the remote branch when pushing.
Create the Pull Request
- Go to the repository on GitHub
- You should see a prompt to create a PR for your recently pushed branch
- Click on “Compare & pull request”
- Fill in the PR description, explaining the change and why it’s needed
- Assign yourself to the PR
- Select the whole Solution Architect team for review
- Submit the PR
Here’s an example of how you might phrase this in your PR description:
## Description
Brief description of changes
## Testing
- [ ] Step 1
- [ ] Step 2
## Screenshots
(if applicable)
## Related Issues
Fixes #123
Summaries don’t have to be massive but it’s good to include things like:
- What issue your PR is related to.
- What change your PR adds.
- How you tested your change.
- Anything you want reviewers to scrutinise.
- Links to other PRs, issues or any other information you think reviewers need.
Great work, your PR is submitted, now wait for the review and any suggested changes and you’ll be ready to hit Merge pull request
Merge pull request
Once reviewed and all looks good hit Merge pull request
on GitHub and throw a message into the team channel requesting the team to approve the MR.
Common Issues & Solutions
Before we move on to cleanup, here are some common situations you might run into and how to handle them:
# Undo last commit (keep changes)
git reset --soft HEAD~1
# Discard all local changes
git reset --hard HEAD
# Update branch with main
git checkout main
git pull
git merge main
Branch cleanup
Once merged you will see a button on Github to delete the branch, go ahead and click this, we want to keep everything clean and not leave stale branches lying around.
On your local machine you will still have the branch you created. You can now delete this with the following steps:
- First, switch to your main branch and pull the latest changes:
git checkout main
git pull origin main
- Prune remote branches:
git fetch --prune
is useful here. It removes any remote-tracking references that no longer exist on the remote. However, a more thorough command is:
This fetches updates from all remotes and prunes dead branches.
git fetch --all --prune
- Delete local branches: To delete branches that have been merged into main run this command:
git branch --merged main | grep -v "^\*" | xargs -n 1 git branch -d
- Lists branches merged into main
- Excludes the current branch
- Deletes each of these branches
- Force delete unmerged branches (if necessary):
If you have local branches that weren’t merged (perhaps due to squash merges on GitHub), you might need to force delete them:
git branch -D branch-name
- Review remaining branches:
git branch -a
This lists all local and remote branches, helping you ensure you’ve cleaned up properly.
Done and dusted 🎉
⚙️🧹Automate cleanup
Here’s a little script to do a git cleanup for you, it’s also on my GitHub:
#!/bin/bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Helper functions
print_header() {
echo -e "\n${BLUE}$1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
# Check if we're in a git repository
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
print_error "Not a git repository!"
exit 1
fi
print_header "Git cleanup crew are starting up 🧹"
# Change to the root directory of the Git repository
cd "$(git rev-parse --show-toplevel)" || exit 1
# Print current repository information
print_header "Current repository:"
echo "Path: $(pwd)"
echo "Remote origin URL: $(git config --get remote.origin.url)"
echo "Current branch: $(git branch --show-current)"
echo
# Determine default branch
DEFAULT_BRANCH=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5)
print_header "Default branch detected as: $DEFAULT_BRANCH"
# Update default branch
git checkout "$DEFAULT_BRANCH"
git pull origin "$DEFAULT_BRANCH"
# Fetch from all remotes and prune dead branches
print_header "Fetching and pruning remote branches..."
git fetch --all --prune
# Function to show branch details
show_branch_details() {
local branch=$1
local last_commit_date=$(git log -1 --format=%cd --date=relative "$branch" 2>/dev/null)
local commit_count=$(git rev-list --count "$branch" ^"$DEFAULT_BRANCH" 2>/dev/null || echo "0")
local author=$(git log -1 --format=%an "$branch" 2>/dev/null)
echo "Branch: $branch"
echo " Last modified: $last_commit_date"
echo " Author: $author"
echo " Unique commits: $commit_count"
echo " Status: $(git log -1 --format=%s "$branch" 2>/dev/null)"
echo
}
# Categorize branches
print_header "Branch Categories:"
# 1. Merged branches
echo "1. Merged branches (safe to delete):"
merged_branches=$(git branch --merged "$DEFAULT_BRANCH" | grep -v "^\*" | grep -v "$DEFAULT_BRANCH")
if [ -n "$merged_branches" ]; then
echo "$merged_branches" | while read -r branch; do
show_branch_details "$branch"
done
else
echo "No merged branches found."
fi
# 2. Unmodified review branches (created but never committed to)
echo -e "\n2. Review branches (created but never modified):"
for branch in $(git branch | grep -v "^\*" | grep -v "$DEFAULT_BRANCH"); do
if [ "$(git rev-list --count "$branch" ^"$DEFAULT_BRANCH")" -eq 0 ]; then
show_branch_details "$branch"
fi
done
# 3. Work in progress branches (have unique commits)
echo -e "\n3. Work in progress branches:"
for branch in $(git branch | grep -v "^\*" | grep -v "$DEFAULT_BRANCH"); do
if [ "$(git rev-list --count "$branch" ^"$DEFAULT_BRANCH")" -gt 0 ]; then
show_branch_details "$branch"
fi
done
# Menu for actions
while true; do
print_header "Cleanup Options:"
echo "1. Delete merged branches"
echo "2. Delete review branches (no unique commits)"
echo "3. Delete specific branches"
echo "4. Show branch details"
echo "5. Exit"
read -p "Select an option (1-5): " option
case $option in
1)
if [ -n "$merged_branches" ]; then
echo "$merged_branches"
read -p "Delete these merged branches? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "$merged_branches" | xargs -r git branch -d
print_success "Merged branches deleted!"
fi
else
print_warning "No merged branches to delete."
fi
;;
2)
review_branches=""
for branch in $(git branch | grep -v "^\*" | grep -v "$DEFAULT_BRANCH"); do
if [ "$(git rev-list --count "$branch" ^"$DEFAULT_BRANCH")" -eq 0 ]; then
review_branches="$review_branches$branch"$'\n'
fi
done
if [ -n "$review_branches" ]; then
echo "$review_branches"
read -p "Delete these review branches? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "$review_branches" | xargs -r git branch -D
print_success "Review branches deleted!"
fi
else
print_warning "No review branches to delete."
fi
;;
3)
read -p "Enter branch name to delete: " branch_name
if git branch | grep -q "$branch_name"; then
show_branch_details "$branch_name"
read -p "Delete this branch? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
git branch -D "$branch_name"
print_success "Branch deleted!"
fi
else
print_error "Branch not found!"
fi
;;
4)
read -p "Enter branch name to show details: " branch_name
if git branch | grep -q "$branch_name"; then
show_branch_details "$branch_name"
else
print_error "Branch not found!"
fi
;;
5)
print_success "Cleanup complete! You're now on the $DEFAULT_BRANCH branch."
exit 0
;;
*)
print_error "Invalid option!"
;;
esac
done
Edit this as you like.
“Nice…. do I put this script in the main repo to run it?”
Nope, stick it in a personal bin directory:
~/bin/git-cleanup
Benefits of this approach:
- It’s available for all your projects.
- It’s not tied to any specific repository.
- If ~/bin is in your PATH, you can run it from anywhere.
To set this up:
- Create the bin directory if it doesn’t exist:
mkdir -p ~/bin
- Move the script there:
mv git-cleanup.sh ~/bin/git-cleanup
- Make it executable:
chmod +x ~/bin/git-cleanup
- Add the bin directory to your PATH if it’s not already there. Add this line to your ~/.bashrc or ~/.zshrc:
export PATH="$HOME/bin:$PATH"
- Reload your shell configuration:
source ~/.zshrc #or ~/.bashrc
Make it a Git alias:
You can also set up the script as a Git alias for even easier use:
Edit your ~/.gitconfig
file and add:
[alias]
cleanup = !$HOME/bin/git-cleanup
Now you can run it with:
git cleanup
There ya go, automated cleanup, nice!
Further Reading
- Really nice article from David Adrian - # Trunk-Based Development with Git
- GitHub flow
- Google Developer Blockly - Writing a Good Pull Request
- Git docs - https://git-scm.com/doc
- There’s a paid “Everything You’ll Need to Know About Git” on FrontendMasters by The Primeaegen that’s good if you like a course (and want to pay)