Your First Pull and Merge Request With a Sprinkle of Automation

Your First Pull and Merge Request With a Sprinkle of Automation

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

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-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

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/merging-a-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:

  1. Create the bin directory if it doesn’t exist:
mkdir -p ~/bin
  1. Move the script there:
mv git-cleanup.sh ~/bin/git-cleanup
  1. Make it executable:
chmod +x ~/bin/git-cleanup
  1. 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"
  1. 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

Related Posts

Books... They Have Been Read!

Books... They Have Been Read!

I’m a collector

I have to admit something… I am a book collector. There you go, it’s true. I buy books and put them on the shelf and this becomes a shelf of shame that I walk past and the books taunt me as I walk past trying not to look too long.

Read More
Outcome Over Output - Measuring Change in Security Education

Outcome Over Output - Measuring Change in Security Education

“100% of employees completed the annual security awareness training 🥳”, “Yay, we are secure 🎊”… 🙃

Read More
Pour out the Acronym Soup - Create Memorable Names

Pour out the Acronym Soup - Create Memorable Names

Wot Do U Call It? - Wiley

The security industry and naming things…

The challenges when it comes to naming anything is nothing new, it’s hard and not to be underestimated.

Read More