我想在Git中重命名/移动项目子树
/project/xyz
Run Code Online (Sandbox Code Playgroud)
至
/components/xyz
Run Code Online (Sandbox Code Playgroud)
如果我使用普通的git mv project components
,那么所有的提交历史xyz project
都会丢失.有没有办法移动这个以保持历史?
Tro*_*sen 622
Git检测重命名而不是使用提交持久化操作,因此无论您使用git mv
还是mv
无关紧要.
该log
命令采用--follow
在重命名操作之前继续历史记录的参数,即,它使用启发式方法搜索类似的内容:
http://git-scm.com/docs/git-log
要查找完整历史记录,请使用以下命令:
git log --follow ./path/to/file
Run Code Online (Sandbox Code Playgroud)
Øys*_*ler 90
它可能重命名文件,并保持完好的历史,但它会导致整个仓库的整个历史要重命名的文件.这可能只适用于强迫性的git-log爱好者,并且有一些严重的影响,包括:
现在,既然你还在和我在一起,那么你可能是一个独立开发人员重命名一个完全孤立的文件.让我们使用移动文件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
Run Code Online (Sandbox Code Playgroud)
将重做分支中的每个提交,在每次迭代的ticks中执行命令.当你这样做时,很多东西都可能出错.我通常会测试文件是否存在(否则它还没有移动)然后执行必要的步骤来按照自己的喜好对树进行修剪.在这里,您可以通过文件来修改对文件的引用,等等.把自己打昏!:)
完成后,文件将被移动并且日志完好无损.你觉得自己像个忍者海盗.
也; 仅当您将文件移动到新文件夹时,才需要mkdir目录.该如果将避免该文件夹的创建较早的历史比你的文件存在.
Tun*_*ble 81
简短的回答是NO,无法在Git中重命名文件并记住历史记录.这是一种痛苦.
谣言有它git log --follow
--find-copies-harder
可以工作,但它对我不起作用,即使文件内容没有任何变化,并且已经进行了移动git mv
.
(最初我使用Eclipse在一个操作中重命名和更新包,这可能会让git感到困惑.但这是一件非常常见的事情.--follow
如果只mv
执行a然后执行a commit
并且mv
不是太远就会起作用.)
Linus说,您应该全面了解软件项目的全部内容,而不需要跟踪单个文件.好吧,可悲的是,我的小脑子不能这样做.
这是真的很烦,所以很多人都盲目地重复声明git的自动追踪移动.他们浪费了我的时间.Git没有这样的事情.按设计(!)Git根本不跟踪移动.
我的解决方案是将文件重命名回原始位置.更改软件以适合源控件.使用git,你似乎只需要第一次正确使用它.
不幸的是,这破坏了似乎使用的Eclipse --follow
.
git log --follow
有时不会显示具有复杂重命名历史的文件的完整历史记录
git log
.(我不知道为什么.)
(有一些太聪明的黑客可以追溯并重新开始做旧工作,但它们相当令人恐惧.请参阅GitHub-Gist:emiller/git-mv-with-history.)
Eri*_*ink 41
git log --follow [file]
Run Code Online (Sandbox Code Playgroud)
将通过重命名向您显示历史记录.
Jam*_*ene 18
我做:
git mv {old} {new}
git add -u {new}
Run Code Online (Sandbox Code Playgroud)
Von*_*onC 16
我想在Git中重命名/移动项目子树
Run Code Online (Sandbox Code Playgroud)/project/xyz
至
/组件/ XYZ
如果我使用plain
git mv project components
,那么项目的所有提交历史xyz
都会丢失.
不(8年后,Git 2.19,Q8 2018),因为Git会检测目录重命名,现在这个记录更好了.
请参阅提交b00bf1c,提交1634688,提交0661e49,提交4d34dff,提交983f464,提交c840e1a,提交9929430(2018年6月27日),并提交d4e8062,提交5dacd4a(2018年6月25日),由Elijah Newren(newren
)提交.
(由Junio C gitster
Hamano合并- -在提交0ce5a69,2017年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)如果重命名将目录拆分为两个或更多其他目录,则重命名最多的目录为"wins".
- b)如果路径是合并任一侧的重命名源,则避免对路径进行目录重命名检测.
- c)如果历史记录的另一侧是重命名的目录,则仅将隐式目录重命名应用于目录.
oli*_*bre 15
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
Run Code Online (Sandbox Code Playgroud)
设置/清除目的地
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning the folder
Run Code Online (Sandbox Code Playgroud)
以电子邮件格式提取每个文件的历史记录
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"' {} ';'
Run Code Online (Sandbox Code Playgroud)
不幸的选择--follow
或--find-copies-harder
不能结合--reverse
.这就是重命名文件时(或重命名父目录时)切断历史记录的原因.
电子邮件格式的临时历史记录:
/tmp/mail/dir
??? subdir
? ??? file3
? ??? file4
??? file5
Run Code Online (Sandbox Code Playgroud)
Dan Bonachea建议在第一步中反转git log generation命令的循环:不是每个文件运行一次git log,而是在命令行上使用一个文件列表运行一次,并生成一个统一的日志.这种方式提交修改多个文件仍然是结果中的单个提交,并且所有新提交都保持其原始相对顺序.请注意,在(现在统一)日志中重写文件名时,还需要在下面的第二步中进行更改.
假设您要在这个其他仓库中移动这三个文件(可以是相同的仓库).
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
Run Code Online (Sandbox Code Playgroud)
因此重新组织您的文件:
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
Run Code Online (Sandbox Code Playgroud)
您的临时历史记录现在是:
/tmp/mail/dir
??? dirB
??? dirB1
? ??? file33
? ??? file44
??? dirB2
??? file5
Run Code Online (Sandbox Code Playgroud)
更改历史记录中的文件名:
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
Run Code Online (Sandbox Code Playgroud)
你的其他回购是:
my_other_repo
??? dirF
? ??? file55
? ??? file56
??? dirH
??? file77
Run Code Online (Sandbox Code Playgroud)
从临时历史文件中应用提交:
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date
Run Code Online (Sandbox Code Playgroud)
--committer-date-is-author-date
保留原始提交时间戳(Dan Bonachea的评论).
你的其他回购现在是:
my_other_repo
??? dirF
? ??? file55
? ??? file56
??? dirB
? ??? dirB1
? ? ??? file33
? ? ??? file44
? ??? dirB2
? ??? file5
??? dirH
??? file77
Run Code Online (Sandbox Code Playgroud)
使用git status
看量的承诺准备推:-)
列出已重命名的文件:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
Run Code Online (Sandbox Code Playgroud)
更多自定义:您可以git log
使用选项--find-copies-harder
或完成命令--reverse
.您还可以使用cut -f3-
和grepping完整模式'{.*=>.*}' 删除前两列.
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'
Run Code Online (Sandbox Code Playgroud)
小智 10
我遇到了“在不丢失历史记录的情况下重命名文件夹”的问题。要修复它,请运行:
$ git mv oldfolder temp && git mv temp newfolder
$ git commit
$ git push
Run Code Online (Sandbox Code Playgroud)
重命名目录或文件(我对复杂情况了解不多,所以可能会有一些警告):
git filter-repo --path-rename OLD_NAME:NEW_NAME
Run Code Online (Sandbox Code Playgroud)
在提到它的文件中重命名目录(可以使用回调,但我不知道如何):
git filter-repo --replace-text expressions.txt
Run Code Online (Sandbox Code Playgroud)
expressions.txt
是一个填充有类似行的文件literal:OLD_NAME==>NEW_NAME
(可以使用 Python 的 RE withregex:
或 glob with glob:
)。
要重命名提交消息中的目录:
git-filter-repo --message-callback 'return message.replace(b"OLD_NAME", b"NEW_NAME")'
Run Code Online (Sandbox Code Playgroud)
也支持 Python 的正则表达式,但它们必须用 Python 手动编写。
如果存储库是原始的,没有远程,则必须添加--force
以强制重写。(在执行此操作之前,您可能希望创建存储库的备份。)
如果您不想保留 refs(它们将显示在 Git GUI 的分支历史记录中),则必须添加--replace-refs delete-no-add
.
小智 7
我按照这个多步骤过程将代码移动到父目录并保留历史记录。
第 0 步:从 'master' 创建一个分支 'history' 用于保管
第 1 步:使用git-filter-repo工具重写历史记录。下面的这个命令将文件夹 'FolderwithContentOfInterest' 移动到一个级别并修改了相关的提交历史
git filter-repo --path-rename ParentFolder/FolderwithContentOfInterest/:FolderwithContentOfInterest/ --force
Run Code Online (Sandbox Code Playgroud)
第 2 步:此时 GitHub 存储库丢失了其远程存储库路径。添加远程参考
git remote add origin git@github.com:MyCompany/MyRepo.git
Run Code Online (Sandbox Code Playgroud)
第 3 步:在存储库上拉取信息
git pull
Run Code Online (Sandbox Code Playgroud)
第四步:连接本地丢失分支和源分支
git branch --set-upstream-to=origin/history history
Run Code Online (Sandbox Code Playgroud)
步骤 5:如果出现提示,解决文件夹结构的合并冲突
第 6 步:推!!
git push
Run Code Online (Sandbox Code Playgroud)
注意:修改后的历史记录和移动的文件夹似乎已经提交。 enter code here
完毕。代码移动到父目录/所需目录保持历史完整!
小智 7
只需使用以下命令移动文件和舞台:
git add .
Run Code Online (Sandbox Code Playgroud)
在提交之前您可以检查状态:
git status
Run Code Online (Sandbox Code Playgroud)
这将显示:
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: old-folder/file.txt -> new-folder/file.txt
Run Code Online (Sandbox Code Playgroud)
我使用 Git 版本 2.26.1 进行测试。
摘自GitHub 帮助页面。
归档时间: |
|
查看次数: |
315606 次 |
最近记录: |