LoginSignup
0
0

More than 1 year has passed since last update.

ローカルリポジトリのコミットから、任意のファイルを抽出する

Last updated at Posted at 2023-03-25

もちろん、mkdir ./export && git archive "commit id" "file path" | tar --extract --directory=./export 推奨
↓を使う理由は特にないですw 逆にデメリットがあるかもしれません。

※注意※
 現在の "git add" を取り消して、ステージ経由でファイルを復元します。
 また最後に "git add" を取り消した分をステージに戻します。
 その過程で何らかのファイルが削除される可能性があります。

実行環境
・Bash
・Ubuntu Server 22.04

git-export_version.1
# 0.変数代入
Commit_ID="コミットID(sha1)"
Want_to_restore="抽出したいファイル"
Export_folder="抽出先にしたいディレクトリ"
Work_tree_path="$PWD"

# 1.変数 初期値設定
Commit_ID="${Commit_ID:-HEAD}"
Want_to_restore="${Want_to_restore:-:/}"
Export_folder="${Export_folder:-Export_folder}"
Work_tree_path="${Work_tree_path:-.}"
git_path="`git -C $Work_tree_path rev-parse --absolute-git-dir`"
work_tree_root="`git -C "$Work_tree_path" rev-parse --show-toplevel`"
stage_backup="${work_tree_root%/}/.stage_backup_checkin"
export_path=`cd "$Work_tree_path" && mkdir -p "$Export_folder" && readlink -f "$Export_folder"`
# 2.バックアップ・抽出
git -C "$work_tree_root" checkout-index --force --prefix="${stage_backup%/}/" --all
git -C "$work_tree_root" rm -r --force --ignore-unmatch --quiet --cached -- :/
git -C "$work_tree_root" restore --staged --source="$Commit_ID" -- $Want_to_restore
git -C "$work_tree_root" checkout-index --force --prefix="${export_path%/}/" --all
git -C "$work_tree_root" rm -r --force --ignore-unmatch --quiet --cached -- :/
# 3.ステージにあったファイルを復元
git --git-dir="$git_path" --work-tree="$stage_backup" add .
# 4.ステージ_バックアップの削除
git -C "$work_tree_root" rm -r --force --ignore-unmatch --quiet -- "$stage_backup" \
   ; rm -rf "$stage_backup"
# 5.抽出したファイルが git の管理除外対象になっていることを確認
# grep --quiet --line-regexp "`basename $export_path`" "$git_path/info/exclude" \
#    || echo `basename "$export_path"` >> "$git_path/info/exclude"
git-export_version.2
# 0.変数代入
Commit_ID="コミットID(sha1)"
Want_to_restore="抽出したいファイル"
Export_folder="抽出先にしたいディレクトリ"
Work_tree_path="$PWD"

# 1.変数 初期値設定
Commit_ID="${Commit_ID:-HEAD}"
Want_to_restore="${Want_to_restore:-:/}"
Export_folder="${Export_folder:-Export_folder}"
Work_tree_path="${Work_tree_path:-.}"
work_tree_root="`git -C "$Work_tree_path" rev-parse --show-toplevel`"
stage_backup="${work_tree_root%/}/.index_backup_tostage"
export_path=`cd "$Work_tree_path" && mkdir -p "$Export_folder" && readlink -f "$Export_folder"`
# 2.バックアップ・抽出
rm -f "$stage_backup"
while read LINE; do
  on_stage_A="`cut -f 1 <<< $LINE`"
  on_stage_B="`cut -f 2 <<< $LINE`"
  echo -e "$on_stage_A"'\t'"\"$on_stage_B\"" >> "$stage_backup"
done < <(git ls-files --stage)
git -C "$work_tree_root" rm -r --force --ignore-unmatch --quiet --cached -- :/
git -C "$work_tree_root" restore --staged --source="$Commit_ID" -- $Want_to_restore
git -C "$work_tree_root" checkout-index --force --prefix="${export_path%/}/" --all
git -C "$work_tree_root" rm -r --force --ignore-unmatch --quiet --cached -- :/
# 3.ステージにあったファイルを復元
git -C "$work_tree_root" update-index --index-info < "$stage_backup"
# 4.ステージ_バックアップの削除
git -C "$work_tree_root" rm --force --ignore-unmatch --quiet -- "$stage_backup" \
   ; rm -f "$stage_backup"
# 5.抽出したファイルが git の管理除外対象になっていることを確認
# grep --quiet --line-regexp "`basename $export_path`" "$git_path/info/exclude" \
#    || echo `basename "$export_path"` >> "$git_path/info/exclude"

詳細

version 1➝2 の変更点
以下のやり方だと blob オブジェクトを新たに追加しない。
git ls-files --stage > "$stage_backup"
 mode   SP   type   SP   sha1 TAB  path
(mode 空白 type 空白 sha1 タブ path)
の形式でインデックスをバックアップ。
git update-index --index-info < "$stage_backup"
バックアップからインデックスに直接記入。


Commit_ID="コミットID(sha1)"
 ⌇⌇⌇
 ⌇⌇⌇
export_path="`cd $Work_tree_path && mkdir -p $Export_folder && readlink -f $Export_folder`"
変数に代入、初期値を用意。最後のは、相対パスだった時に絶対パスに変換するもの。

git_path="`git -C $work_tree_root rev-parse --absolute-git-dir`"
$GIT_DIR 及び .git のフルパス("--path-format=absolute --git-dir")を取得する。
work_tree_root="`git -C "$Work_tree_path" rev-parse --show-toplevel`"
--show-toplevel は既定で絶対パスを返す。


git -C "$work_tree_root"
git コマンドを実行する場所を指定。


git checkout-index --force --prefix="${stage_backup%/}/" --all
stage 上のファイルを .stage_backup にコピー。


git rm -r --force --ignore-unmatch --quiet --cached -- `git ls-files`
対象を .git/index から削除し、stage から降ろす( stage を空にする、work_tree からはファイルを削除しない)。


git restore --stage --source="$Commit_ID" -- $Want_to_restore
指定したコミットから、指定したファイルを stage 上に戻す。


git checkout-index --force --prefix="${export_path%/}/" --all
stage 上に戻したファイルを $Export_folder にコピー。


git rm -r --force --ignore-unmatch --quiet --cached -- `git ls-files`
.git/index から削除し、stage を空にする。


git --git-dir="$git_path" --work-tree="$stage_backup" add .
.stage_backup を work_tree に設定し、
ファイルを .stage_backup から stage に上げる。


git rm -r --force --ignore-unmatch --quiet "$stage_backup" \
  ; rm -rf "$stage_backup"
念のため .git/index から .stage_backup 全体を削除し、work_tree からもファイルとディレクトリを削除する。さらに、git 管理されていない場合の削除も work_tree で実行する。


grep --quiet --line-regexp "`basename $export_path`" "$git_path/info/exclude" \
  || echo "`basename $export_path`" >> "$git_path/info/exclude"
$Export_folder を git 管理対象から除外する。


備考

git -C "$Work_tree_path" checkout-index --force --prefix="${stage_backup%/}/" --all
git -C "$Work_tree_path" rm -r --force --ignore-unmatch --quiet --cached -- `git -C "$Work_tree_path" ls-files`
git --git-dir="$git_path" --work-tree="$stage_backup" add .
git -C "$Work_tree_path" rm -r --force --ignore-unmatch --quiet -- "$stage_backup" \
   ; rm -rf "$stage_backup"

これらは、以下で代用できるかもしれない(ただし、work_tree も消える可能性がある)。
 (※[-S --staged] は "Git 2.35" 以上)
git stash push --staged --all --message "stage_backup"
git stash pop --index

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