Chat GPTにshellを書かせてみた

AI に shell を書かせてみたらどうなるか、試しにやってみました。

今回書いてもらうのは、[前回の記事](https://qiita.com/sekappy_official/items/c8a31620c9cf080a9e8a)でご紹介した Git ブランチの整理を自動化する shell スクリプトです。
https://qiita.com/sekappy_official/items/c8a31620c9cf080a9e8a

::: note warn
### AI を利用する際の注意
ChatGPT などの AI を利用するにあたって、1つだけ注意点があります。

それは、**業務に関する機密情報を外部に漏らしてはいけない** ということです。
AI を利用するにあたっては、以下のような工夫をしましょう。

* 機密に抵触しない程度の作業に利用する。
* 既存コードや重要な用語／固有名詞を貼るのを避ける。
* ある程度のプロトタイプを作成してもらうに留めて、重要な部分は自分の手で修正する。
:::
# Step1. develop ブランチを最新化する
まずは ChatGPT に最初の指示を出します。

>**筆者**:
>
>_以下2つのディレクトリにある Git プロジェクトを一括操作する shell スクリプトを作って頂けますか？_
>
>`~/project/api`
>`~/project/front`
>
>それぞれ最新の develop ブランチの内容を pull したいです。_

>**ChatGPT**:
>
>以下のようなシェルスクリプトを作成することで、指定された2つのGitプロジェクトの develop ブランチを一括で pull できます。このスクリプトでは、ディレクトリを移動して develop ブランチをチェックアウトし、その後 pull 操作を行います。

## スクリプト: pull_develop.sh
    #!/bin/bash

    # プロジェクトディレクトリのパス
    API_DIR=~/project/api
    FRONT_DIR=~/project/front
    
    # APIプロジェクトの更新
    echo "Updating API project..."
    cd $API_DIR || { echo "Failed to change directory to $API_DIR"; exit 1; }
    git checkout develop || { echo "Failed to checkout develop branch in $API_DIR"; exit 1; }
    git pull origin develop || { echo "Failed to pull from develop branch in $API_DIR"; exit 1; }
    
    # Frontプロジェクトの更新
    echo "Updating Front project..."
    cd $FRONT_DIR || { echo "Failed to change directory to $FRONT_DIR"; exit 1; }
    git checkout develop || { echo "Failed to checkout develop branch in $FRONT_DIR"; exit 1; }
    git pull origin develop || { echo "Failed to pull from develop branch in $FRONT_DIR"; exit 1; }
    
    echo "Both projects are up to date."
## スクリプトの使用方法
>**ChatGPT**:
>
>1. 上記の内容を `pull_develop.sh` というファイル名で保存します。
>2. スクリプトに実行権限を付与します。

    chmod +x pull_develop.sh

>**ChatGPT**:
>3. スクリプトを実行します。

    ./pull_develop.sh

>**ChatGPT**:
>
>このスクリプトは、2つのプロジェクトディレクトリに移動して、 develop ブランチを pull するので便利です。エラーが発生した場合は、その都度エラーメッセージが表示され、スクリプトの実行が中断されます。

**ヤバい、ちゃんと出来てる！　しかもエラー処理もメッセージ出力もある！！**
でもまだまだ甘い。 function を使って共通化しないとね。

>**筆者**:
>
>`$API_DIR` や `$FRONT_DIR` を引数に取る function を作って処理を共通化できますか？

>**ChatGPT**:
>
>もちろん、`$API_DIR` と `$FRONT_DIR` を引数に取る関数を作成して処理を共通化できます。以下のように書き換えると、ディレクトリを引数として受け取り、そのディレクトリ内で共通の処理を実行する関数を使用したスクリプトになります。

## 改良版スクリプト: pull_develop.sh
    #!/bin/bash
    
    # Git プロジェクトの develop ブランチを pull する関数
    pull_develop_branch() {
      local project_dir=$1

      echo "Updating project in $project_dir..."
    
      # 指定されたディレクトリに移動し、ブランチのチェックアウトと pull を実行
      cd $project_dir || { echo "Failed to change directory to $project_dir"; exit 1; }
      git checkout develop || { echo "Failed to checkout develop branch in $project_dir"; exit 1; }
      git pull origin develop || { echo "Failed to pull from develop branch in $project_dir"; exit 1; }
    }
    
    # APIプロジェクトとFrontプロジェクトをそれぞれ更新
    pull_develop_branch ~/project/api
    pull_develop_branch ~/project/front
    
    echo "Both projects are up to date."
## スクリプトの使い方は以前と同じです
>**ChatGPT**:
>
>1. 上記の内容を `pull_develop.sh` というファイル名で保存。
>2. 実行権限を付与。

    chmod +x pull_develop.sh
>**ChatGPT**:
>
>3. スクリプトを実行。

    ./pull_develop.sh
>**ChatGPT**:
>
>このスクリプトでは、`pull_develop_branch` という関数にディレクトリを渡し、その中でブランチの切り替えと pull を行っています。これにより、共通処理が関数化され、コードがより簡潔になりました。

すごい、やれてる！　**しかもここまでやるのに3分かかっていない。**

# Step2. 不要なブランチを削除する
>**筆者**:
>
>`pull_develop_branch` を改良して、不要なブランチを削除する処理を追加してください。
>“feature/” で始まるブランチのうち、 develop ブランチにマージ済みのものを削除したいです。

>**ChatGPT**:
>
>`pull_develop_branch` 関数に、feature/ で始まるブランチのうち develop ブランチにマージ済みのものを削除する処理を追加します。この処理は、`git branch --merged` コマンドを使用して develop にマージ済みのブランチをリストし、その中から feature/ で始まるブランチを削除するようにします。
>
>以下に改良版のスクリプトを示します。

## 改良版スクリプト: pull_develop.sh
    #!/bin/bash
    
    # Git プロジェクトの develop ブランチを pull し、不要な feature ブランチを削除する関数
    pull_develop_branch() {
      local project_dir=$1
    
      echo "Updating project in $project_dir..."
    
      # 指定されたディレクトリに移動し、ブランチのチェックアウトと pull を実行
      cd $project_dir || { echo "Failed to change directory to $project_dir"; exit 1; }
    
      # develop ブランチに切り替え
      git checkout develop || { echo "Failed to checkout develop branch in $project_dir"; exit 1; }
    
      # develop ブランチを最新に更新
      git pull origin develop || { echo "Failed to pull from develop branch in $project_dir"; exit 1; }
    
      # develop にマージ済みの feature/ ブランチを削除
      echo "Removing merged feature branches..."

      # マージ済みのブランチを取得し、feature/ で始まるものを削除
      git branch --merged develop | grep 'feature/' | while read -r branch; do
        if [ "$branch" != "develop" ]; then
          git branch -d "$branch" && echo "Deleted branch $branch"
        fi
      done
    }
    
    # APIプロジェクトとFrontプロジェクトをそれぞれ更新し、不要なブランチを削除
    pull_develop_branch ~/project/api
    pull_develop_branch ~/project/front
    
    echo "Both projects are up to date, and merged feature branches are deleted."
## スクリプトの解説
>**ChatGPT**:
>
>1. `git branch --merged develop`: develop ブランチにマージ済みのブランチをリストアップします。
>2. `grep 'feature/'`: その中から feature/ で始まるブランチのみをフィルタリングします。
>3. `git branch -d`: develop にマージ済みの feature/ ブランチをローカルから削除します。
>4. develop 自体は削除しないようにチェックしています。

## スクリプトの使い方は同じです
>**ChatGPT**:
>
>+ スクリプトを保存して実行することで、develop ブランチを pull し、feature/ で始まるマージ済みのブランチを削除する処理が実行されます。

あっという間にスクリプトが完成していく。
「**[前回の記事](https://qiita.com/sekappy_official/items/c8a31620c9cf080a9e8a)、わざわざ書く必要があったのか？**」という考えが頭をよぎるが、いったん忘れて指示を続ける。

>**筆者**:
>
>さらに `pull_develop_branch` を改良してください。
>
>– 関数名を `cleanup_repository` にしてください。
>– リモートで削除されているブランチをローカルからも削除してください。

>**ChatGPT**:
>
>以下のように関数名を `cleanup_repository` に変更し、リモートで削除されたブランチもローカルから削除する処理を追加しました。この変更により、`git fetch -p` を使ってリモートで削除されたブランチをローカルでも自動的に削除することができます。

## 改良版スクリプト: pull_develop_v2.sh
    #!/bin/bash
    
    # Git プロジェクトの develop ブランチを pull し、不要な feature ブランチとリモートで削除されたブランチをローカルからも削除する関数
    cleanup_repository() {
      local project_dir=$1
    
      echo "Cleaning up repository in $project_dir..."
    
      # 指定されたディレクトリに移動
      cd $project_dir || { echo "Failed to change directory to $project_dir"; exit 1; }
    
      # リモートの最新情報を取得し、リモートで削除されたブランチもローカルから削除
      git fetch -p || { echo "Failed to fetch updates for $project_dir"; exit 1; }
    
      # develop ブランチに切り替え
      git checkout develop || { echo "Failed to checkout develop branch in $project_dir"; exit 1; }
    
      # develop ブランチを最新に更新
      git pull origin develop || { echo "Failed to pull from develop branch in $project_dir"; exit 1; }

      # develop にマージ済みの feature/ ブランチを削除
      echo "Removing merged feature branches..."
    
      git branch --merged develop | grep 'feature/' | while read -r branch; do
        if [ "$branch" != "develop" ]; then
          git branch -d "$branch" && echo "Deleted branch $branch"
        fi
      done
    
      echo "Repository cleanup complete for $project_dir."
    }
    
    # APIプロジェクトとFrontプロジェクトをそれぞれ更新し、不要なブランチを削除
    cleanup_repository ~/project/api
    cleanup_repository ~/project/front
    
    echo "Both projects are up to date, and cleaned up."
## 新しい追加ポイント
>**ChatGPT**:
>
>1. `git fetch -p`: リモートで削除されたブランチをローカルからも削除します（-p オプションで prune 操作を行い、リモート追跡ブランチをローカルから削除）。
>2. 関数名を `cleanup_repository` に変更し、リポジトリ全体のクリーンアップ操作を示すようにしました。

## スクリプトの使用方法
>**ChatGPT**:
>
>前回と同様にスクリプトを実行するだけで、リモートブランチの削除も含めた全体のクリーンアップを実行できます。

そうか、 `--prune` ではなくて `-p` でも良いんだ。**勉強になるなぁ……！**

>**筆者**:
>
>`“git fetch -p”` を実行したあと、リモートで削除されたブランチを参照していたローカルブランチを削除できますか？

>**ChatGPT**:
>
>`git fetch -p` を実行した後、リモートで削除されたブランチを参照していたローカルブランチを削除するには、`git branch -vv` コマンドを使用して、リモート追跡ブランチが “gone” と表示されているローカルブランチをリストし、それを削除することができます。
>
>以下に、その処理を含めた改良版スクリプトを示します。これにより、リモートで削除されたブランチを参照しているローカルブランチも自動的に削除します。

## 改良版スクリプト: pull_develop_v3.sh
    #!/bin/bash
    
    # Git プロジェクトの develop ブランチを pull し、不要な feature ブランチとリモートで削除されたブランチをローカルからも削除する関数
    cleanup_repository() {
      local project_dir=$1
    
      echo "Cleaning up repository in $project_dir..."
    
      # 指定されたディレクトリに移動
      cd $project_dir || { echo "Failed to change directory to $project_dir"; exit 1; }
    
      # リモートの最新情報を取得し、リモートで削除されたブランチもローカルから削除
      git fetch -p || { echo "Failed to fetch updates for $project_dir"; exit 1; }
    
      # develop ブランチに切り替え
      git checkout develop || { echo "Failed to checkout develop branch in $project_dir"; exit 1; }
    
      # develop ブランチを最新に更新
      git pull origin develop || { echo "Failed to pull from develop branch in $project_dir"; exit 1; }
    
      # develop にマージ済みの feature/ ブランチを削除
      echo "Removing merged feature branches..."
    
      git branch --merged develop | grep 'feature/' | while read -r branch; do
        if [ "$branch" != "develop" ]; then
          git branch -d "$branch" && echo "Deleted branch $branch"
        fi
      done
    
      # リモートで削除されたブランチを参照しているローカルブランチを削除
      echo "Removing local branches tracking deleted remote branches..."
    
      git branch -vv | grep ': gone]' | awk '{print $1}' | while read -r branch; do
        git branch -d "$branch" && echo "Deleted branch $branch (tracking remote branch deleted)"
      done
    
      echo "Repository cleanup complete for $project_dir."
    }
    
    # APIプロジェクトとFrontプロジェクトをそれぞれ更新し、不要なブランチを削除
    cleanup_repository ~/project/api
    cleanup_repository ~/project/front
    
    echo "Both projects are up to date, and cleaned up."
## 新しい追加ポイント
>**ChatGPT**:
>
>1. `git branch -vv | grep ': gone]'`: リモートブランチが削除されている (gone と表示されている) ローカルブランチをリストします。
>2. `awk '{print $1}'`: ローカルブランチの名前を抽出します。
>3. `git branch -d "$branch"`: リモートで削除されたブランチを参照していたローカルブランチを削除します。

## スクリプトの使用方法
> **ChatGPT**:
>
>以前と同様に実行するだけで、リモートで削除されたブランチを追跡していたローカルブランチも含めてクリーンアップを行います。

所要時間15分足らず。これで完成です。**やっぱり俺、要らないんじゃね？** 😇

# まとめ
ChatGPT を使って、 Git 管理を自動化する shell を作成しました。
自分が作ったものと同等のものがわずか15分で完成して、正直びっくりしました。

ただし、これは「素人でもできる」ものではなく、ある程度の知識を要求されるものだと感じました。

Git の仕様をよく理解していないと「何をやらせるか」を指示できませんし、
「shell で function を作れる」という知識がないと「共通化して」という指示を出せません。

自分自身は引き続き勉強して知識を身に着けつつ、 AI を上手く使って作業を効率化していきたいものです。

※本記事はChat GPTによる回答を引用して執筆しております。