Gitのプロジェクトサブツリーの名前を変更/移動したい
/project/xyz
に
/components/xyz
プレーンを使用するgit mv project components
と、のすべてのコミット履歴xyz project
が失われます。履歴が維持されるようにこれを移動する方法はありますか?
Gitのプロジェクトサブツリーの名前を変更/移動したい
/project/xyz
に
/components/xyz
プレーンを使用するgit mv project components
と、のすべてのコミット履歴xyz project
が失われます。履歴が維持されるようにこれを移動する方法はありますか?
回答:
Gitはあなたが使用しそうかどうか、かなりコミットを使用して操作を持続するよりも、名前の変更を検出したgit mv
か、mv
問題ではありません。
このlog
コマンドは、--follow
名前変更操作の前に履歴を継続する引数を取ります。つまり、ヒューリスティックを使用して同様のコンテンツを検索します。
http://git-scm.com/docs/git-log
完全な履歴を検索するには、次のコマンドを使用します。
git log --follow ./path/to/file
git config alias.logf "log --follow"
書き込むだけgit logf ./path/to/file
です。
ファイルの名前を変更して履歴をそのまま保持することは可能ですが、リポジトリの履歴全体でファイルの名前が変更されます。これはおそらく強迫的なgit-log-loversのためだけのものであり、以下を含むいくつかの深刻な影響があります。
さて、あなたは私と一緒にいるので、あなたは完全に孤立したファイルの名前を変更するおそらく一人の開発者です。を使用してファイルを移動しましょうfilter-tree
!
ファイルold
をフォルダに移動dir
して名前を付けると仮定しますnew
これはで行うことができますgit mv old dir/new && git add -u dir/new
が、それは歴史を壊します。
代わりに:
git filter-branch --tree-filter 'if [ -f old ]; then mkdir dir && mv old dir/new; fi' HEAD
ブランチのすべてのコミットをやり直し、各反復のティックでコマンドを実行します。これを行うと、多くの問題が発生する可能性があります。私は通常、ファイルが存在するかどうかを確認し(そうでない場合、まだ移動する必要はありません)、必要な手順を実行して、好みに合わせてツリーをシューホーンします。ここで、ファイルへの参照を変更するために、ファイル全体をsedする場合があります。ノックアウト!:)
完了すると、ファイルが移動され、ログはそのまま残ります。あなたは忍者海賊のように感じます。
また; もちろん、mkdir dirは、ファイルを新しいフォルダーに移動する場合にのみ必要です。もしあなたのファイルが存在するよりも、歴史の中で前にこのフォルダの作成を避けることができます。
--index-filter
、ツリーをチェックアウトしてコミットするたびに戻す必要がないため、for renamesを使用するとはるかに高速になります。--index-filter
各コミットインデックスに直接作用します。
短い答えはNOです。Gitでファイルの名前を変更して履歴を記憶することはできません。そしてそれは苦痛です。
噂でgit log --follow
--find-copies-harder
はうまくいくと思いますが、ファイルの内容に変更がなく、移動がで行われたとしても、私にはうまくいきませんgit mv
。
(当初、私はGitのを混同している可能性が一回の操作で、パッケージの名前を変更し、更新するためにEclipseを使用していました。しかし、それは行うには非常に一般的なものである。--follow
場合にのみ動作するようには思えないmv
行った後、さcommit
とmv
遠くではありません。)
Linusは、個々のファイルを追跡する必要はなく、ソフトウェアプロジェクトの内容全体を全体的に理解することになっていると言います。残念ながら、私の小さな脳はそれを行うことができません。
Gitが動きを自動的に追跡するという発言を非常に多くの人々が無意識に繰り返してきたのは本当にうっとうしいことです。彼らは私の時間を無駄にしました。Gitはそのようなことをしません。設計上(!)Gitは動きをまったく追跡しません。
私の解決策は、ファイルの名前を元の場所に戻すことです。ソース管理に合わせてソフトウェアを変更します。Gitを使用すると、最初からそれを "git"する必要があるようです。
残念ながら、それはを使用しているように見えるEclipseを壊します--follow
。git log --follow
複雑な名前変更履歴があるファイルの完全な履歴が表示されない場合がありますgit log
。(何故かはわからない。)
(以前の作業に戻って再コミットする巧妙なハックがいくつかありますが、恐ろしいものです。GitHub -Gist:emiller / git-mv-with-historyを参照してください。)
私がやります:
git mv {old} {new}
git add -u {new}
-A
代わりにの動作が必要ですか?繰り返しますが、こちらをご覧ください:git-scm.com/docs/git-add
git add -u
。Gitのドキュメントは役に立たない傾向があり、私が見たい最後の場所です。これgit add -u
が実際に表示されている投稿の1つです:stackoverflow.com/a/2117202。
Gitのプロジェクトサブツリーの名前を変更/移動したい
/project/xyz
に
/ components / xyz
プレーンを使用する
git mv project components
と、xyz
プロジェクトのすべてのコミット履歴が失われます。
いいえ(8年後、Git 2.19、2018年第3四半期)。Gitがディレクトリ名の変更を検出するためです。これはドキュメント化が進んでいます。
参照b00bf1cコミット、1634688をコミット、0661e49コミット、4d34dffコミット、983f464コミット、c840e1aコミット、9929430コミット(2018年6月27日に)、そしてd4e8062コミット、5dacd4aコミットにより(2018年6月25日)をエリヤNewren( )newren
。
(合併によりJunio C浜野- gitster
-で0ce5a69コミットし、2018年7月24日)を
それは今で説明されていDocumentation/technical/directory-rename-detection.txt
ます:
例:
すべての場合には
x/a
、x/b
およびx/c
に移動してきたz/a
、z/b
そしてz/c
、それは可能性がありx/d
、その間に追加もに移動したいz/d
ディレクトリ全体が「というヒント取ってx
」に移動する「z
」を。
ただし、次のような他の多くのケースがあります。
履歴
x -> z
の一方の名前は、もう一方は一部のファイルの名前をに変更x/e
するため、マージで推移的な名前変更を行う必要があります。
ディレクトリの名前変更の検出を簡略化するために、これらのルールはGitによって適用されます。
ディレクトリ名の変更の検出が適用される場合、いくつかの基本的なルール制限があります。
- 特定のディレクトリがマージの両側にまだ存在する場合、名前が変更されたとは見なされません。
- 名前を変更するファイルのサブセットにファイルまたはディレクトリがある場合(または相互に邪魔になる場合)は、それらの特定のサブパスのディレクトリの名前変更を「オフ」にし、ユーザーに競合を報告します。 。
- 履歴の反対側でディレクトリの名前を変更して、履歴の反対側で名前を変更した場合、暗黙的なディレクトリの名前変更については、履歴の反対側からの特定の名前変更を無視します(ただし、ユーザーに警告します)。
で多くのテストを見ることができますがt/t6043-merge-rename-directories.sh
、それはまたそれを指摘しています:
- a)名前の変更によってディレクトリが他の2つ以上に分割される場合、最も多くの名前が変更されたディレクトリが「優先」されます。
- b)パスがマージのいずれかの側で名前変更のソースである場合、パスのdirectory-rename-detectionを回避します。
- c)履歴の反対側が名前の変更を行う側である場合にのみ、暗黙のディレクトリ名変更をディレクトリに適用します。
git am
git log --pretty=email -p --reverse --full-index --binary
cat extracted-history | git am --committer-date-is-author-date
例: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 the folder
各ファイルの履歴をメール形式で抽出する
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
Dan Bonacheaは、最初のステップでgit log生成コマンドのループを反転させることをお勧めします。ファイルごとにgit logを1回実行するのではなく、コマンドラインにファイルのリストを指定して1回だけ実行し、単一の統合ログを生成します。この方法では、複数のファイルを変更するコミットは結果の単一のコミットのままであり、すべての新しいコミットは元の相対順序を維持します。(今は統合された)ログのファイル名を書き換える場合は、次の2番目のステップで変更が必要になることに注意してください。
これら3つのファイルをこの別のリポジトリ(同じリポジトリにすることもできます)に移動するとします。
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB # New tree
│ ├── dirB1 # from subdir
│ │ ├── file33 # from file3
│ │ └── file44 # from file4
│ └── dirB2 # new dir
│ └── file5 # from file5
└── dirH
└── file77
したがって、ファイルを再編成します。
cd /tmp/mail/dir
mkdir -p dirB/dirB1
mv subdir/file3 dirB/dirB1/file33
mv subdir/file4 dirB/dirB1/file44
mkdir -p 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"' {} ';'
あなたの他のリポジトリは:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
一時履歴ファイルからコミットを適用します。
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date
--committer-date-is-author-date
元のコミットのタイムスタンプを保持します(Dan Bonacheaのコメント)。
あなたの他のリポジトリは今です:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB
│ ├── dirB1
│ │ ├── file33
│ │ └── file44
│ └── dirB2
│ └── file5
└── dirH
└── file77
git status
プッシュする準備ができているコミットの量を確認するために使用します:-)
名前が変更されたファイルを一覧表示するには:
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 '{.* => .*}'
このマルチステッププロセスに従って、コードを親ディレクトリに移動し、履歴を保持しました。
ステップ0:保管のために「master」からブランチ「history」を作成しました
ステップ1:git-filter-repoツールを使用して履歴を書き換えます。以下のこのコマンドは、フォルダー 'FolderwithContentOfInterest'を1レベル上に移動し、関連するコミット履歴を変更しました
git filter-repo --path-rename ParentFolder/FolderwithContentOfInterest/:FolderwithContentOfInterest/ --force
ステップ2:この時までに、GitHubリポジトリはリモートリポジトリパスを失いました。リモート参照を追加
git remote add origin git@github.com:MyCompany/MyRepo.git
ステップ3:リポジトリーの情報をプルする
git pull
手順4:ローカルの失われたブランチを元のブランチに接続する
git branch --set-upstream-to=origin/history history
手順5:メッセージが表示された場合、フォルダー構造のマージの競合に対処する
ステップ6:プッシュ !!
git push
注:変更された履歴と移動されたフォルダーは、既にコミットされているようです。 enter code here
できました。コードは親/目的のディレクトリに移動し、履歴はそのまま残ります!
GitのコアであるGit配管は名前の変更を追跡しませんが、Gitログ「磁器」で表示する履歴は、必要に応じてそれらを検出できます。
指定git log
された-Mオプションを使用します。
git log -p -M
Gitの現在のバージョン。
これは、他のコマンドでgit diff
も同様に機能します。
比較を多少厳密にするオプションがあります。同時にファイルに大幅な変更を加えずにファイルの名前を変更すると、Gitログや友人が名前の変更を検出しやすくなります。このため、あるコミットでファイルの名前を変更し、別のコミットでファイルを変更する人もいます。
Gitにファイルの名前が変更された場所を見つけるように頼むときはいつでも、CPUの使用にコストがかかるため、それを使用するかどうか、いつ使用するかはあなた次第です。
特定のリポジトリの名前変更検出を使用して常に履歴を報告したい場合は、以下を使用できます。
git config diff.renames 1
あるディレクトリから別のディレクトリに移動するファイルが検出されます。次に例を示します。
commit c3ee8dfb01e357eba1ab18003be1490a46325992
Author: John S. Gruber <JohnSGruber@gmail.com>
Date: Wed Feb 22 22:20:19 2017 -0500
test rename again
diff --git a/yyy/power.py b/zzz/power.py
similarity index 100%
rename from yyy/power.py
rename to zzz/power.py
commit ae181377154eca800832087500c258a20c95d1c3
Author: John S. Gruber <JohnSGruber@gmail.com>
Date: Wed Feb 22 22:19:17 2017 -0500
rename test
diff --git a/power.py b/yyy/power.py
similarity index 100%
rename from power.py
rename to yyy/power.py
これは、だけでなく、diffを使用しているときはいつでも機能することに注意してくださいgit log
。例えば:
$ git diff HEAD c3ee8df
diff --git a/power.py b/zzz/power.py
similarity index 100%
rename from power.py
rename to zzz/power.py
試用として、機能ブランチの1つのファイルに小さな変更を加えてコミットし、次にマスターブランチでファイルの名前を変更してコミットし、ファイルの別の部分に小さな変更を加えてコミットしました。機能ブランチに行ってマスターからマージすると、マージによりファイルの名前が変更され、変更がマージされました。これはマージからの出力です:
$ git merge -v master
Auto-merging single
Merge made by the 'recursive' strategy.
one => single | 4 ++++
1 file changed, 4 insertions(+)
rename one => single (67%)
その結果、ファイルの名前が変更され、両方のテキストが変更された作業ディレクトリができました。したがって、Gitは明示的に名前変更を追跡しないにもかかわらず、Gitが正しいことを行うことができます。
これは古い質問への回答が遅いため、他の回答はその時点のGitバージョンでは正しかった可能性があります。
まず、名前を変更するだけでスタンドアロンコミットを作成します。
次に、ファイルの内容に対する最終的な変更は、個別のコミットに入れられます。
ディレクトリまたはファイルの名前を変更するには(私は複雑なケースについてあまり知らないので、いくつかの注意事項があるかもしれません):
git filter-repo --path-rename OLD_NAME:NEW_NAME
それを言及しているファイルのディレクトリの名前を変更するには(コールバックを使用することは可能ですが、方法がわかりません):
git filter-repo --replace-text expressions.txt
expressions.txt
のような行で満たされたファイルですliteral:OLD_NAME==>NEW_NAME
(PythonのREを使用しregex:
たり、グロブを使用したりすることが可能ですglob:
)。
コミットのメッセージでディレクトリの名前を変更するには:
git-filter-repo --message-callback 'return message.replace(b"OLD_NAME", b"NEW_NAME")'
Pythonの正規表現もサポートされていますが、Pythonで手動で記述する必要があります。
リポジトリがリモートなしのオリジナルの場合、--force
強制的に書き換えて追加する必要があります。(これを行う前に、リポジトリのバックアップを作成することをお勧めします。)
refを保持したくない場合(それらはGit GUIのブランチ履歴に表示されます)、を追加する必要があります--replace-refs delete-no-add
。
ファイルを移動し、次のようにステージングするだけです。
git add .
コミットする前に、ステータスを確認できます。
git status
それが表示されます:
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: old-folder/file.txt -> new-folder/file.txt
Gitバージョン2.26.1でテストしました。
GitHubヘルプページから抽出。
ファイルを移動させてから
git add -A
これにより、削除されたすべての新しいファイルがサテージング領域に配置されます。ここでgitはファイルが移動されたことを認識します。
git commit -m "my message"
git push
理由はわかりませんが、これでうまくいきます。
git mv
:stackoverflow.com/questions/1094269/whats-the-purpose-of-git-mv