0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

shell で Git ブランチの整理を自動化する

Last updated at Posted at 2024-12-10

Git を使った開発をやっていると、手動でブランチ管理をするのが面倒になることがあります。
筆者が困っていたのは以下の2点についてでした。

  • develop ブランチを毎日最新にしたいが、毎日同じようなコマンドを実行するのが面倒。
  • 古いブランチがローカルに溜まっていて、いちいち削除するのが面倒。

今までは手動でこれらの管理をしていたのですが、そろそろ自動化しようと思います。

なお、私の開発環境では WSL 環境(Ubuntu)を使っています。
幸いにも shell スクリプトが使えますので、これを使って自動化をしましょう。

この記事で学べること

shell スクリプトの書き方のうち、以下について学べます。

  • 処理を共通化する書き方 : function
  • ループ処理の書き方 : for ... in
  • 2つ以上のコマンドを連携させるやり方 : パイプ(|)、 xargs

ディレクトリ構成

開発で使っているディレクトリの構成は以下のようなものです。

/home/username/project/api
/home/username/project/front

api, front の2つの Git リポジトリが存在しています。
それぞれのリポジトリについて、不要なブランチを削除する shell を作成してみましょう。

Step1. develop ブランチを最新化する

試しに develop ブランチを更新する shell を考えます。

#!/bin/sh

cd ~/project/api

# api の develop を最新にする。
git checkout develop
git pull

cd ~/project/front

# front の develop を最新にする。
git checkout develop
git pull

develop ブランチを checkout して pull するだけの簡単な処理です。

これでも動作するのですが、同じ処理をコピー&ペーストしているのが気になります。
function を作って共通化しましょう。

Step.1 で完成したスクリプト

#!/bin/sh

function refresh_repository() {
    local TARGET_DIR="$1"
    cd "TARGET_DIR"

    # develop を最新にする。
    git checkout develop
    git pull
}

refresh_repository ~/project/api
refresh_repository ~/project/front

refresh_repository という function を作りました。第1引数 $1 でディレクトリを受け取っています。

develop ブランチを最新化する処理としては、ひとまずこれで良いでしょう。

Step2. 不要なブランチを削除する

続いて、不要なブランチを削除する処理を追加してみます。

先ほど作った refresh_repository に機能を追加しましょう。

function refresh_repository() {
    local TARGET_DIR="$1"
    cd "TARGET_DIR"

    # develop を最新にする。
    git checkout develop
    git pull

    # リモートで削除されたブランチを削除する。
    git remote prune origin
}

まず git pull コマンドに --prune オプションを追加します。
これはローカルに残ったリモート追跡ブランチを削除するオプションです。
これと git remote prune を組み合わせることで、リモートで削除されたブランチを一括削除できるようになります。

しかし、リモートでブランチが削除されないケースについては対処できていません。

develop にマージ済みのブランチのうち、 feature/... で始まるものは削除したいです。
この条件を満たすブランチを列挙するには、以下のコマンドが使えます。

$ git branch --list --merged="develop"  "feature/*"

> feature/1000
> feature/1001
> ...

git branch -d でこれらのブランチを削除しましょう。
先ほど列挙したブランチを別のコマンドに渡すために xargs を使います。

git branch --list --merged="develop" "feature/*" | xargs git branch -d

# xargs により、以下のようなコマンドが順番に実行される。
# git branch -d feature/1000
# git branch -d feature/1001 

xargs は、標準入力で受け取った値をコマンドに渡すコマンドです。
git branch --list で取得したブランチを git branch -d で削除できるようになりました。

これで問題ないように見えますが、1つだけちょっとした問題があります。
削除するべきブランチがないときに、以下のエラーが発生するのです。

fatal: branch name required

これは git branch -d をブランチ名の指定なしで実行しているために起こります。
これを防ぐためには xargs-r オプションを指定します。

git branch --list ... | xargs git branch -d
↓
git branch --list ... | xargs -r git branch -d

-r オプションを指定すると、標準出力が空の時にコマンドが実行されません。
これでエラーメッセージが出なくなります。

では完成したコマンドを refresh_repository に追加しましょう。

Step. 2 で完成したスクリプト

#!/bin/sh

function refresh_repository() {
    local TARGET_DIR="$1"
    cd "TARGET_DIR"

    # develop を最新にする。
    git checkout develop
    git pull --prune

    # リモートで削除されたブランチを削除する。
    git remote prune origin
    # develop ブランチにマージ済みの feature ブランチを削除する。
    git branch --list --merged="develop" "feature/*" | xargs -r git branch -d
}

refresh_repository ~/project/api
refresh_repository ~/project/front

Step3. ブラッシュアップする

せっかくなので、他にも気になる点を修正してみます。

とりあえず気になるところといえば、ディレクトリの指定を個別で書いているところです。

refresh_repository ~/project/api
refresh_repository ~/project/front

project 配下のディレクトリをループで処理すると、きれいに書けそうです。

#!/bin/sh

BASE_DIR=~/project

function refresh_repository() {
    # ... (省略)
}

for project in $(ls $BASE_DIR) ; do
    refresh_repository "$BASE_DIR/$project"
done

ls $BASE_DIR コマンドで処理対象のディレクトリを探し、ループするよう修正しました。

さらに refresh_repository 本体についても改良の余地があります。
develop ブランチを変数にして、 master や他の名前のブランチも処理できるようにしましょう。

最終的に完成したスクリプト

#!/bin/sh

BASE_DIR=~/project
BASE_BRANCH=develop

function refresh_repository() {
    local TARGET_DIR="$1"
    cd "TARGET_DIR"

    # BASE_BRANCH がローカルに存在しないかも知れないので、先に fetch しておく。
    git fetch

    # ブランチを最新にする。
    git checkout "$BASE_BRANCH"
    git pull --prune

    # リモートで削除されたブランチを削除する。
    git remote prune origin

    # BASE_BRANCH ブランチにマージ済みの feature ブランチを削除する。
    # ただし BASE_BRANCH そのものは削除の対象外にする。(--no-contains)
    git branch --list \
        --merged="$BASE_BRANCH" \
        --no-contains="$BASE_BRANCH" \
        "feature/*" \
        | xargs -r git branch -d
}

最後のコマンドは長くなりすぎたので、 \ を使って途中に改行を入れました。

まとめ

shell を使って git のブランチ管理を楽にするスクリプトを作成しました。
また、 shell について以下の書き方をご紹介しました。

  • function で処理を共通化する。
  • ls で取得した結果を for ... in でループ処理をする。
  • xargs とパイプ「|」を使い、あるコマンドの実行結果を元に、別のコマンドを繰り返し実行する。

最初の shell を作るのは少々手間ですが、一度作ってしまえばその後の作業がぐっと楽になります。
もしかしたら shell に苦手意識を持っている方もいるかも知れませんが、この機会に是非触れてみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?