この回答はgit am
、例に基づいて、例を使用して提示された興味深いコマンドを段階的に提供します。
目的
- 一部またはすべてのファイルを1つのリポジトリから別のリポジトリに移動したい。
- あなたは彼らの歴史を守りたいのです。
- ただし、タグやブランチを保持する必要はありません。
- 名前を変更したファイル(および名前を変更したディレクトリ内のファイル)の限られた履歴を受け入れます。
手順
- を使用して履歴を電子メール形式で抽出する
git log --pretty=email -p --reverse --full-index --binary
- ファイルツリーを再編成し、履歴のファイル名の変更を更新する(オプション)
- を使用して新しい履歴を適用する
git am
1.履歴をメール形式で抽出する
例:file3
、file4
およびの履歴を抽出するfile5
my_repo
├── dirA
│ ├── file1
│ └── file2
├── dirB ^
│ ├── subdir | To be moved
│ │ ├── file3 | with history
│ │ └── file4 |
│ └── file5 v
└── dirC
├── file6
└── file7
一時ディレクトリの宛先をクリーンアップします
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning
あなたのレポきれいにソースを
git commit ... # Commit your working files
rm .gitignore # Disable gitignore
git clean -n # Simulate removal
git clean -f # Remove untracked file
git checkout .gitignore # Restore gitignore
各ファイルの履歴をメール形式で抽出する
cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'
残念ながらオプション--follow
または--find-copies-harder
と組み合わせることはできません--reverse
。これが、ファイルの名前が変更されたとき(または親ディレクトリの名前が変更されたとき)に履歴が削除される理由です。
後:電子メール形式の一時的な履歴
/tmp/mail/dir
├── subdir
│ ├── file3
│ └── file4
└── file5
2.ファイルツリーを再編成し、履歴のファイル名の変更を更新します(オプション)。
これら3つのファイルをこの別のリポジトリ(同じリポジトリにすることもできます)に移動するとします。
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB # New tree
│ ├── dirB1 # was subdir
│ │ ├── file33 # was file3
│ │ └── file44 # was file4
│ └── dirB2 # new dir
│ └── file5 # = file5
└── dirH
└── file77
したがって、ファイルを再編成します。
cd /tmp/mail/dir
mkdir dirB
mv subdir dirB/dirB1
mv dirB/dirB1/file3 dirB/dirB1/file33
mv dirB/dirB1/file4 dirB/dirB1/file44
mkdir dirB/dirB2
mv file5 dirB/dirB2
一時的な履歴は次のとおりです:
/tmp/mail/dir
└── dirB
├── dirB1
│ ├── file33
│ └── file44
└── dirB2
└── file5
履歴内のファイル名も変更します。
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
注:これにより、パスとファイル名の変更を反映するように履歴が書き換えられます。
(つまり、新しいリポジトリ内の新しい場所/名前の変更)
3.新しい履歴を適用する
あなたの他のリポジトリは:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
一時履歴ファイルからコミットを適用します。
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am
あなたの他のリポジトリは今です:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB ^
│ ├── dirB1 | New files
│ │ ├── file33 | with
│ │ └── file44 | history
│ └── dirB2 | kept
│ └── file5 v
└── dirH
└── file77
git status
プッシュする準備ができているコミットの量を確認するために使用します:-)
注:パスとファイル名の変更を反映するように履歴が書き直されたため
(つまり、前のリポジトリ内の場所/名前と比較されます)
git mv
場所/ファイル名を変更する必要はありません。
git log --follow
完全な履歴にアクセスする必要はありません。
追加のトリック:リポ内で名前が変更されたファイルや移動されたファイルを検出する
名前が変更されたファイルを一覧表示するには:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
その他のカスタマイズ:git log
オプション--find-copies-harder
またはを使用してコマンドを完了することができます--reverse
。また、cut -f3-
完全なパターン '{。* =>。*}' を使用して最初の2つの列を削除することもできます。
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'