惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Troy Hunt's Blog
Blog — PlanetScale
Blog — PlanetScale
Engineering at Meta
Engineering at Meta
F
Full Disclosure
Recorded Future
Recorded Future
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
GbyAI
GbyAI
博客园_首页
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Recent Commits to openclaw:main
Recent Commits to openclaw:main
H
Hacker News: Front Page
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
博客园 - 司徒正美
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
Help Net Security
Help Net Security
Cloudbric
Cloudbric
PCI Perspectives
PCI Perspectives
有赞技术团队
有赞技术团队
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
TaoSecurity Blog
TaoSecurity Blog
L
Lohrmann on Cybersecurity
量子位
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tailwind CSS Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
B
Blog RSS Feed
Apple Machine Learning Research
Apple Machine Learning Research
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
N
News and Events Feed by Topic
罗磊的独立博客
T
Threat Research - Cisco Blogs
Schneier on Security
Schneier on Security
T
Tor Project blog
IT之家
IT之家
M
MIT News - Artificial intelligence
S
Security @ Cisco Blogs
O
OpenAI News
AI
AI
S
Securelist
Simon Willison's Weblog
Simon Willison's Weblog
The Last Watchdog
The Last Watchdog
月光博客
月光博客
Security Archives - TechRepublic
Security Archives - TechRepublic
L
LINUX DO - 热门话题

git

请教一个诡异的 git 问题 - V2EX 有一个 git 仓库合并问题,不知道怎么办才好 分析一个技巧让同事不知道我使用了 ai : git 忽略本地改动文件,实现不提交 Rebased, 一个 git 客户端 发现一个邪修快速清理 Git 项目里面空文件夹的方法🤡 不知道有没有什么其他标准做法. - V2EX zig 写的 100kb 的 wasm 可以 http 读写任意 git 仓库 - V2EX 有人把 IDEA 的 git 客户端做出来了 问个关于 rebase 和 github pr 的奇怪的问题 - V2EX idea 同款 git 客户端求推荐 - V2EX 有没有类似 JB 家的 Git 管理工具 - V2EX 大家自己的代码都是放在哪儿 - V2EX 分享一个在 Linux 上编译静态 Git 二进制的项目 - V2EX 上亿的 git 仓库如何做冷热存储分离呢 - V2EX 发现了一个极度臃肿的项目 - V2EX 有什么支持直接连接远程主机 git 仓库的 GUI 工具吗 - V2EX KFCode 官网上线,做好用、靠谱、有趣的 Git 托管平台 - V2EX git 工作流,大家现在用的什么样的? - V2EX 很奇怪的一个现象,有没人知道是怎么回事,所有 LLM 都说 100%不会出现 - V2EX 我宣布,最好的 git 客户端是腾讯家的 ugit - V2EX Fork 付费版有什么不同? - V2EX 一位高级工程师的 GIT 需要熟悉到什么程度? - V2EX 已经 push 到远程仓库的提交,如何修改某个用户的所有提交的邮箱啊 - V2EX [求助下] 关于代码同步问题 - V2EX 一直不理解 Windows 下 git 的这个逻辑,我自己 clone 的仓库还不能删了? rm -force 也不行 - V2EX 分享一个 Git 存储库治理利器 - hot - V2EX 代码有没有必要备份到多个远程仓库?比如 github 和 codeup。有必要的话最好怎么备份? - V2EX 请教 git 里怎么删除记录 - V2EX git 中如何将子分支的多个提交作为一个提交合并到主分支? - V2EX git 切换分支问题 - V2EX git 各种命令执行很慢是什么原因导致的? - V2EX 有没有大佬指导一下 git 问题 - V2EX git remotes 分支克隆 - V2EX 两处修改需要分开提交吗? - V2EX Linux 的 Ubuntu 系统有类似 Sourcetree 或者 fork 这种 git 图形化操作的客户端工具吗 - V2EX 关于刚刚 git 的问题描述的不清楚,不能编辑主题了,重新发下问题: git 如何对比服务器上最新的代码和本地的区别? git diff 对比和我预想不一样 - V2EX git 新手求教: git 如何对比服务器上最新的代码和本地的区别?不是本地远端和本地 working。 svn 可以使用 show log,直接对比,调用 beyond compare 很方便。 - V2EX 对上游提 pr,上游的管理员觉得 pr 里面的功能他不需要或者不满意 - V2EX gitee fork 时继承推送规则是否合理? - V2EX 请教各位关于 Git 合并的问题 - V2EX 请教大家一个测试环境代码合并的问题 - V2EX 推送远程仓库导致本地提交记录消失,如何找回 - V2EX 给上游 pr,自己应该先 pr 到自己的 main 分支吗? - V2EX 请教一个开发流程中 GIT 解决冲突的问题 - V2EX 求教:在 fork 的仓库上添加不太可能被 upstream 接受的修改,是不是应该在新开的 branch 上开发? - V2EX 有哪个 git 的 gui 软件。可以像 idea 的 git 管理那样查看文件历史和代码冲突?现在换到了 cursor,但是这边的代码管理个人用的太不习惯了。 - V2EX git clean 还能找回吗 - V2EX 求助 git 自动 merge 丢代码 - V2EX 码云代码自动同步到 github - V2EX 请教大家这样的项目应该要怎么做 git 管理 - V2EX 从本地提交代码到 gitlab 后, diff 异常问题,求大佬解惑 - V2EX 在 Git 中,已知某一个分支的某个 Commit 引入了一个 bug,如何快速确定这个 bug 是经过哪些分支流转到 master 分支的? git rebase 那么重要么??? 如何在 git 提交前将生产版本和开发版本的配置进行区分 开源了个新的版本控制系统 HugeSCM,请 V 友们指导 Git Permission denied 问题求助
问个 Git 基操:怎么样复制一个文件,能保持历史记录? - V2EX
xiangyuecn · 2025-05-23 · via git

这是一个创建于 388 天前的主题,其中的信息可能已经有所发展或是发生改变。

git cp:没这号命令😂
git mv:这是改名

比如已有 a.txt ,我现在要个 b.txt 。 如何复制出 b.txt 这个文件,并且复制前的历史和 a.txt 保持一致?

还是说 和空文件夹 一样,git 不在乎你死活?

操作尽量简单,要是涉及的非基础知识点太多,还是算了,这历史不要也罢😂

第 1 条附言  ·  2025 年 5 月 23 日

好了,测试了一下,直接复制文件 b.txt ,不要修改内容,直接先提交 b.txt 个新文件,然后再去修改后提交

再使用 git log --follow b.txt 就能看到所有历史记录了。TortoiseSVN 中使用“遍历方法”下拉选项“跟踪重命名”,也可以看到所有记录。

- 如果复制了马上修改文件,再提交,就关联不上历史记录
- 只要新文件和当前版本中已有的文件相同,就可以稳定追踪历史记录
- 已有文件修改后,再复制出新文件,只要提交时两个文件内容相同,一起提交后,同样可以稳定追踪历史记录

-----------

结论:不需要任何操作,直接复制文件,提交,完事,注意不要改复制的文件就行

参考:@dcsuibian #45 @B1ankCat #73 @justplaymore #75 @mayli #70

第 2 条附言  ·  2025 年 5 月 23 日

修正 "TortoiseSVN" -> "TortoiseGit"
修正 @dcsuibian #45

  • git
  • 复制
  • 历史记录

    99 条回复    2025-05-24 09:19:28 +08:00

    benjim

    2

    benjim      2025 年 5 月 23 日

    你这需求就很妖

    way2create

    3

    way2create      2025 年 5 月 23 日

    什么应用场景这是,我只试过批量修改提交历史的用户名邮箱,因为搞错了

    xiangyuecn

    4

    xiangyuecn      2025 年 5 月 23 日

    @skankhunt42 @cookygg
    场景一:a.txt 里面有 500 行代码,现在有个新功能 b.txt ,和 a.txt 功能完全一致,就只用改某一行的几个字

    场景二:a.txt 里面有 2000 行代码,现在要把里面后半部分 1000 行拆分到 b.txt 里,拆分出来的代码新文件要有原来的历史记录

    darklinden

    5

    darklinden      2025 年 5 月 23 日

    我这儿有过需求是 mono repo 中两个子项目共用一个配置
    解决方式是单独提出来到最外层,然后如果需要放到子项目内就在文件夹内丢硬链接

    pagxir

    6

    pagxir      2025 年 5 月 23 日 via Android

    你确实这是用 git 来做版本管理的么,你确定不是想建立一个分支啥的。非要 copy 的话,你可以考虑用 SVN 。

    Iakihsoug

    8

    Iakihsoug      2025 年 5 月 23 日

    啊这 不知道 OP 听没听说过继承重写

    magggia

    9

    magggia      2025 年 5 月 23 日

    没戏,跟 git 自身的原理相悖了,不支持

    mercury233

    10

    mercury233      2025 年 5 月 23 日

    假如 a 文件涉及 100 个 commit ,你这新建 b 文件的同时也得新建 100 个 commit ,不能去修改历史记录,我猜 git 自身没有这个功能

    unused

    11

    unused      2025 年 5 月 23 日 via Android   ❤️ 2

    filter-branch 每个 commit 复制一遍

    whoosy

    12

    whoosy      2025 年 5 月 23 日   ❤️ 1

    git 本事就是保持和维护 commit 历史记录的,你这直接伪造可还行

    ningxing

    14

    ningxing      2025 年 5 月 23 日

    新建分支就行了啊,不同分支不同记录,只是你只关心 a 和 b 文件,其他文件不用关心

    xiangyuecn

    15

    xiangyuecn      2025 年 5 月 23 日

    @magggia “没戏,跟 git 自身的原理相悖了,不支持”
    但又存在 git mv 这个命令,mv 本身按字面意思就是删文件+新建文件,要实现复制只是不需要删文件+新建文件

    BruceXu

    17

    BruceXu      2025 年 5 月 23 日   ❤️ 3

    我算是看明白了.
    假设 a 文件是 1 年前创建的,有 100 条修改记录.

    OP 想的是,根据 a 文件复制出一个 b 文件来.同时在 git 中可以看到 b 文件也是 1 年前创建的,也有这 100 条修改记录.

    这不是造假么...

    C02TobNClov1Dz56

    18

    C02TobNClov1Dz56      2025 年 5 月 23 日   ❤️ 4

    简单, a 文件保留, 然后 b 文件开头加一行注释, 历史见 a 文件(

    mangoDB

    19

    mangoDB      2025 年 5 月 23 日

    你想让「文件 b 」和「文件 a 」具备一样的历史记录,不仅仅是内容的拷贝。我觉得`git`应该无法满足你这个奇怪的需求,这相当于是“伪造”了「文件 b 」的历史记录(仿佛开启了月报宝盒)。

    如楼上的几个答案所说,这和“版本控制”的理念是矛盾的。

    xiangyuecn

    20

    xiangyuecn      2025 年 5 月 23 日

    @whoosy @xubingok “伪造” “造假”
    比如:新文件 b.txt 提交的时候会有新建文件的记录,显示是从 a.txt 复制。就像 mv 一样 显示从 a.txt 改名 新文件名可以看到以前的老文件名记录

    维护一下这个记录很难的嘛🙏

    Trim21

    21

    Trim21      2025 年 5 月 23 日 via Android

    做不到,这个要看对应的 git 客户端能不能识别出他是文件改名。

    geelaw

    22

    geelaw      2025 年 5 月 23 日   ❤️ 6

    https://devblogs.microsoft.com/oldnewthing/20190919-00/?p=102904

    基本原理是:

    - blame 在 merge commit 处会追踪文件来自各个 parents 的部份。
    - 让文件 a 和 b 来自 merge 的两个 parents 。
    - 让文件 a 来自更早的 a 。
    - 仍文件 b 来自更早的 a 。

    我的做法和 Raymond Chen 不一样,假设当前在 branch "current":

    1. 重命名当前文件

    git checkout -b copy
    git mv a _
    git commit -m 'Prepare to copy file.'

    2. 把当前文件在第一个分支上恢复为原来的名字

    git checkout -b copy1
    git mv _ a
    git commit -m 'Restore name of file.'

    3. 把当前文件在另一个分支上重命名为副本的名字

    git checkout -b copy2 copy
    git mv _ b
    git commit -m 'Copy file.'

    4. 合并两个修改

    git checkout copy
    git merge --no-ff -m 'Finish copying file.' copy1 copy2

    5. 回归原分支

    git checkout current
    git merge --no-ff -m 'Copy `a` to `b`.' copy

    6. 删除中间分支

    git branch -d copy copy1 copy2

    sunbeams001

    23

    sunbeams001      2025 年 5 月 23 日   ❤️ 1

    把 a.txt 相关的所有记录导出成文本,贴在创建 b.txt 的 commit message 里

    geelaw

    24

    geelaw      2025 年 5 月 23 日   ❤️ 4

    @magggia #9
    @mercury233 #10
    @mangoDB #19
    见 #22

    @xubingok #17 这当然不是造假,考虑各种变种需求:文件拆分成两个,希望各自保留历史;两个文件合并,希望保留各自的历史。

    @Trim21 #21 模糊识别重命名确实有这个问题,但是 Git 的原理保证:如果一个 commit 里面只发生不同内容文件的重命名(没有内容修改、没有多个同内容文件重命名),那么历史可以正确用 blame 追溯。(不满足此条件则会有模糊匹配的问题,所以我给文件改名的时候都是一个 commit 只做改名一件事的。)

    gesse

    25

    gesse      2025 年 5 月 23 日

    本身 git 的哲学就是“保持历史真实性”,你这个需求就是“篡改历史”

    相违背了。

    94

    30

    94      2025 年 5 月 23 日

    @xiangyuecn #15 ,mv 是重命名啊,怎么到你这边就变成删文件+新建文件了……
    基于“文件”进行操作,给这个 mv 操作添加到对应文件的 commit 历史中。

    git-mv - Move or rename a file, a directory, or a symlink
    [Git - git-mv Documentation]( https://git-scm.com/docs/git-mv)

    如果按照 OP 你的意思,其实复制出来一个 b 文件。但对于你来说是知道这个文件是基于 a 文件复制出来的,但是对于 git 来说 b 文件单纯只是没有被追踪过的一个新文件。
    所以你的实际操作是新增一个 b 文件,同时去按照 a 文件的编辑历史造 b 文件的整个历史,而不是共享整个历史。

    loon98

    33

    loon98      2025 年 5 月 23 日

    这种需求应该做成子仓库, txt 文件保留两份 blame 指向同一个记录, 必须由分支实现. 主仓库分别引用子仓库的两个分支.
    你要求的这个用法就不符合 git 思想和哲学, 要么就用 24l 的方案, 要么就用最符合 git 哲学的办法达成近似效果.

    codehz

    34

    codehz      2025 年 5 月 23 日 via Android

    @dfkjgklfdjg git mv 等价于 mv+git rm+git add ,git 识别文件移动完全靠猜测,并没有特别记录这个信息

    feedcode

    35

    feedcode      2025 年 5 月 23 日

    11 楼老哥都给你答案了,再给你个详细的
    git filter-branch --tree-filter 'cp -f dir/a1 dir/a3' --tag-name-filter cat --prune-empty -- --all

    94

    36

    94      2025 年 5 月 23 日

    @dfkjgklfdjg #30 ,不管是使用分支冲突的方式,还是通过 filter-branch/repo 去创造 b 文件的历史,都会去修改仓库的提交历史。在多人协作的项目里面操作 git 历史一个非常危险的行为。

    xiangyuecn

    37

    xiangyuecn      2025 年 5 月 23 日

    @geelaw #22 感谢,前几天 deepseek 也给出了类似的方案,我就是嫌操作起来太复杂了,要是有 git cp 一条命令解决,这个世界就安静了,这记录如果没有简单操作实现,其实不要历史也没什么关系,提交的时候 commit 里面手动备注是从 a.txt 复制来的,到了这个位置就手动切换到 a.txt 查看历史

    94

    38

    94      2025 年 5 月 23 日

    @codehz #34 ,识别到 rename 是猜。但是如果猜到了,那么提交历史里面是会有标注是 rename ,而不是 add 。

    ```
    diff --git a/test.xt b/test2.xt
    similarity index 100%
    rename from test.xt
    rename to testTT.xt
    ```

    geelaw

    39

    geelaw      2025 年 5 月 23 日   ❤️ 1

    @xiangyuecn #37 git cp 很难成立,原因:git mv 和 git rm 都不产生 commit ,而是修改 index 等,因此基于一致性,git cp 也应该不产生 commit 而只是修改 index ;但是 git 数据库里面每个 commit 存的是状态,而不是“怎么来的”,因此要让 git blame 认识到一个文件是另一个的副本,要么 git blame 自动检测(见下一段),要么这个信息必须存在于 commits 的历史中,用多分支的做法就是把这个信息存放在历史中,这必然要创建多个 commits ,因此 git cp 不会这样做。

    目前的实现里,如果一个 commit 的惟一变化是有一个新文件 B ,并且新文件 B 和某个已经存在的文件 A 内容一样,那么 Git 会认为新文件是“手工重新写出来的”,不会认为 B 来自于 A 。我觉得这样设计的原因是很多配置文件可能确实会是内容上一样的,但更可能是基于不同的原因分别写出来的,所以不宜合并历史。

    git mv 也并不保证 git blame 会认为移动前后的文件有关联性,这种信息依然是 Git 通过对比两个 commits 算出来的。

    94

    40

    94      2025 年 5 月 23 日

    @dfkjgklfdjg #38 ,随便建了一个 test 文件,rename 的时候顺手加了个 TT 。回复的时候感觉好像改成 test2 更合适。就手动改成了 2 ,但直接发出来了,后面的 to 还没改掉。

    😂 将就看吧。

    codehz

    41

    codehz      2025 年 5 月 23 日 via Android

    @dfkjgklfdjg 这个 diff 完全是根据两个状态对比实时计算出来的,git 并没有记录任何 diff 信息

    rb6221

    42

    rb6221      2025 年 5 月 23 日   ❤️ 1

    git 原生的概念是基于行操作的,甚至没有整个源文件 mv 的识别。
    mv 的识别都是各大 git 第三方客户端自己实现的,为了用户的交互友好。
    我曾经就遇到过,同一个提交,在某个客户端上被识别为 mv ,在另一个客户端上被识别为 del+add 。没有任何理由。
    整个原理基本上是基于识别同名文件,对比文件中的内容。
    你这样,文件名变了,内容也变了,那鬼才知道你的意图。

    geelaw

    43

    geelaw      2025 年 5 月 23 日

    @dfkjgklfdjg #30 git mv 和删除文件再新建没有任何区别。你在 #38 也说了,Git 是猜的,但

    >但是如果猜到了,那么提交历史里面是会有标注是 rename

    这是错误的,commits 里不会标注为 rename ,只有计算 diff/blame 的时候 Git 才会去(根据 commits 的内容)识别重命名关系。

    codehz

    44

    codehz      2025 年 5 月 23 日 via Android

    @janus77 并没有基于行,是单纯整个文件对比,之所以可以提交 hunk ,也是前端的处理方式,内部生成虚拟文件提交到树里,这个意义上二进制文件和文本文件的唯一区别只在前端展示方面(后端影响的只有可压缩性,二进制一般不太容易压缩)

    dcsuibian

    45

    dcsuibian      2025 年 5 月 23 日

    啊?这个很简单啊
    首先你复制一下文件,复制以后立马就 add 并 commit (这其中不能修改,否则 git 会认为是另一个文件)
    然后就只要
    git log --follow -- 对应的文件
    就好了呀

    TsubasaHanekaw

    46

    TsubasaHanekaw      2025 年 5 月 23 日

    git mv 重命名文件 ,提交之后,回滚,到上一条记录,再重新添加旧的文件, merge 两个分支

    mercury233

    47

    mercury233      2025 年 5 月 23 日

    GIT_AUTHOR_DATE=$(date -d'2012-12-12 01:00:01') GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git commit -m '2012 init'

    git 本身倒是确实支持随意设置提交时间(和提交人),但 git log 的顺序还是按实际顺序的,除非 merge

    ykrank

    49

    ykrank      2025 年 5 月 23 日   ❤️ 1

    @xiangyuecn #29 并不会因为你不在乎重力,你就可以直接飞起来。现在的问题是其他人推荐让你可以飞的工具,你说我一定要靠肉身,然后别人说重力是这个世界的规则,你说我不需要“规则”,只需要飞起来。
    那别人就只能建议你睡一觉或者去天台了

    skiy

    50

    skiy      2025 年 5 月 23 日

    跟修改邮箱一个样。历史记录是不变,但是,所有的 commit id 都变了。

    mangoDB

    52

    mangoDB      2025 年 5 月 23 日   ❤️ 2

    @xiangyuecn #29 非常震惊你会以这种傲慢的态度进行回复。我后悔自己浪费时间码字来回复你(包括这条)。

    B1ankCat

    53

    B1ankCat      2025 年 5 月 23 日

    个人朴素的看法,如果没有固定的命令的话,那么 commit 也是一个 git 对象,它的第一行是 tree ,第二行是 parent ,tree 就是这个文件在目录树中的内容,因为每次 commit 都和 a 一样,所以只需要复制每次 commit 里用到 tree 改下名字,sha1 都一样就行,然后把复制这个 commit ,把第一行改成复制出来的 tree 的 sha1 ,然后把 parent 也这样递归操作,就伪造好一个一模一样的历史了,浅见哈,有错请指出

    gesse

    54

    gesse      2025 年 5 月 23 日   ❤️ 1

    @xiangyuecn #29 非常震惊你会以这种傲慢的态度进行回复。我后悔自己浪费时间码字来回复你(包括这条)。

    passive

    55

    passive      2025 年 5 月 23 日 via Android   ❤️ 1

    git 的优点就是能改历史。但是楼主这题有地触碰到 git 的短板了。

    (爬了楼,什么狗屁哲学和理念,就是 Torvalds 随手写的一个工具)

    moudy

    56

    moudy      2025 年 5 月 23 日 via iPhone   ❤️ 1

    @mangoDB 旁观者说一句,你们上来的用词才是傲慢。又是理念 又是伪造的,好像 lz 要对 git 下黑手一样。lz 只不过是问如何看历史的时候能知道 b 文件是在某个时候从 a copy 出来的,这不是一个很清楚也完全可以理解的需求么,怎么就跟伪造历史扯上了。

    94

    57

    94      2025 年 5 月 23 日

    @geelaw #43 ,好的,修正了认知上的错误。
    所以实际上 git 只会存储变更的文件快照,而不会额外存储变更信息。每次在拉取 log 或者分析的时候实时去对比计算。
    一直以为 git log 输出的内容是直接输出的 commit 中的记录,而不是实时算的。怪不得要加上 --stat 。

    mangoDB

    58

    mangoDB      2025 年 5 月 23 日   ❤️ 1

    @moudy 你甚至都没有理解楼主的需求,就在这里大言不惭的教育别人。楼主需要让「文件 b 」具备和「文件 a 」一样的历史记录,并不是关注「文件 b 」是什么时间点被“拷贝”出来的。

    evada

    59

    evada      2025 年 5 月 23 日

    说 git blame 的意思就是,你使用 git mv a.txt b.txt 然后 commit 。
    然后可以通过 git log --graph --decorate --oneline --name-status --follow b.txt 查看包括 a.txt 的详细记录。
    或者 git blame -C -C -w b.txt 查看每一行的变更记录(包括来自 a.txt)的

    j869716

    60

    j869716      2025 年 5 月 23 日

    谢谢, 已 block

    xiangyuecn

    61

    xiangyuecn      2025 年 5 月 23 日

    @mangoDB #58 主题里面 [写的是] “并且复制前的历史和 a.txt 保持一致?”,复制前的记录两个文件保持一致。 [没说] 没说复制之后去修改 b.txt 的记录,也要去两个文件记录保持一致 [没说] 。难绷😐

    xiangyuecn

    64

    xiangyuecn      2025 年 5 月 23 日

    @lnbiuc @63 “欢迎 fork 此仓库,就叫 xiangyuecn-git 如何” 不感兴趣,欢迎提点有建设性的意见。

    对于源码管理,一个复制的文件,文件本身是有来源的也有历史记录,但 git 没法简单做到这个复制,这本身就是少了点什么

    在本地文件目录里面,直接复制一个新文件改改提交,使用场景中规中矩,可能很多时候不会去在意这个历史记录而已

    kneo

    65

    kneo      2025 年 5 月 23 日

    没记错的话 hg 和 svn 都是会复制文件历史。这是个非常实用的功能。

    没想到某些 git 用户也有饭圈行为啊,一遇到自家没有的功能,就开始扯什么“理念”“造假”。不过能说出这些话的我估计也不是什么正儿八经的用户。

    msg7086

    66

    msg7086      2025 年 5 月 23 日

    文件更名就是删除+添加。git 能追溯,是在看到 diff 并发现删除+添加这样的行为以后,去手动关联起两个文件的历史轨迹。至于为什么复制不能追溯,这个不是 git 本身的局限,而是 git 客户端(也就是 git 软件)的局限,这些软件只会在删除+添加的时候匹配文件,不能在纯添加的时候匹配文件。
    要实现这个功能,不是改 git 协议/数据库,而是要改 git 客户端。你用什么客户端,你就得改什么客户端,或者另外自己去实现这个功能。而且这个更改只有用改过的客户端才能看到,别人用他们自己的客户端照样看不到。

    至于为什么大多数(或者所有)客户端不实现这个功能,多半是因为计算量太大了。原本是在删除和添加的文件里做文字匹配,现在变成了所有文件和添加的文件去做匹配,假如你 git 库里有 10 万个文件,现在添加了 1 个新文件,不就要做 10 万次匹配?性能就跟不上了。

    dandel1on

    67

    dandel1on      2025 年 5 月 23 日

    感觉这是悖论,假设有这个功能,那么你把 b.txt 回退到某个历史节点(这个历史节点来自 a.txt ),结果却没有 b.txt 这个文件,因为那个时候 b.txt 还不存在。

    lnbiuc

    68

    lnbiuc      2025 年 5 月 23 日

    @xiangyuecn #64
    你觉得产品不能满足你的需求,你可以自己选择开发,有开源精神欢迎提 pr ,不想开源自己用也 OK
    别人不可能为你的场景专门进行开发,没有义务也没有必要
    git 作为 linus 的开源项目,并没有禁止 fork ,你觉得有用那就自己做这个功能
    《你不需要理念,你不需要哲学》用别人的东西就得顺着别人来,不服就自己 fork 开发

    lnbiuc

    69

    lnbiuc      2025 年 5 月 23 日

    @kneo 这很常见啊,看看 linux 系对 systemd 的争议就知道了,你总不能说坚持 linux 一次做好一件事理念的这群人,linux 内核的开发者,不是正儿八经的用户吧
    没有 linus 的理念,linux 都不知道成啥样子了

    mayli

    70

    mayli      2025 年 5 月 23 日

    感觉技术上是可行的,因为 git 本身其实对于改名这个操作不是太在意。
    可以这样想
    你直接做两个 branch ,一个 git cp a.txt b.txt, 一个 git mv a.txt c.txt, 然后两个 branch merge ,这样你就有个完整
    的 history 了
    git 是有概率可以侦测到的,但是 git log --follow 也只能是靠猜,毕竟 git log 是跟着路径走
    你 git log a.txt b.txt c.txt 就能看到三个文件的 log 了

    FinnBai

    71

    FinnBai      2025 年 5 月 23 日   ❤️ 2

    其实技术上是可行的,无论是修改历史 commit ,还是 merge ,但楼主“嫌操作起来太复杂”,就是要一个 “git cp” 命令,别人说啥都不听,git 没有就是 git 的问题,这不是巨婴吗

    yyj08070631

    72

    yyj08070631      2025 年 5 月 23 日

    @baiyi 哈哈哈哈我也是想表达类似的观点

    我估计 op 把楼上所说的“造假”理解成了一种批评,但这里说的造假只是出于 git 版本控制的出发点,历史记录是不应该被伪造的,所以 git 当然不会提供这种便捷的操作来让你伪造提交记录

    但既然这些东西都是存在于文件系统,那当然会有解法,只是会麻烦一点

    justplaymore

    75

    justplaymore      2025 年 5 月 23 日

    git log --follow 新文件

    显示新文件的提交记录,包括新文件重命名之前的提交记录。

    wangyzj

    76

    wangyzj      2025 年 5 月 23 日

    git 本身就是记录所有操作历史
    包括你的操作
    历史不动那违背了 git 的初衷

    moudy

    77

    moudy      2025 年 5 月 23 日

    @dfkjgklfdjg #57 所以要是 git 客户端能直接猜出 b 文件是 a 文件的 copy ,lz 可以直接无脑 cp 完事。而且似乎 git log --follow newfile.txt 真可以让 git 主动猜。

    zhuisui

    79

    zhuisui      2025 年 5 月 23 日

    相比于 --follow 你还可以用 --follow --follow ,甚至是显式地使用 --find-copies 甚至是 --find-copies-harder ,此时你可能还需要考虑使用 -l 和 --combined-all-paths 等等

    显然你自己不会去翻文档的,也不会理解 git 如何工作,对吗

    chimission

    80

    chimission      2025 年 5 月 23 日   ❤️ 1

    haha 明明是一个很有意思的技术需求, 结果一堆人在那扯哲学不要这么做, 就像工作了好多年但是技术没什么没长进的老油条找借口推脱不会做的需求一样(狗头)

    xiangyuecn

    81

    xiangyuecn      2025 年 5 月 23 日

    @zhuisui #79 翻文档也得找到到地方,时间成本太高,前几天问 deepseek ,给的解决方案太复杂
    实际压根什么都不需要做就能解决,前提是要知道怎么规避会产生影响的地方,完事

    上面还有个让我 fork git 源码,自己实现这个功能😂

    lnbiuc

    83

    lnbiuc      2025 年 5 月 23 日

    @xiangyuecn #81 这不就是巨婴吗
    显然你自己不会去翻文档的,也不会理解 git 如何工作,对吗

    qping

    84

    qping      2025 年 5 月 23 日

    请忽略我的 @,眼神不好,楼主的是复制,不是重命名

    geelaw

    86

    geelaw      2025 年 5 月 23 日

    @xiangyuecn #78 我不确定为什么你认为 TortoiseGit 能识别 = 这是一个 Git 的基本操作。TortoiseGit 并不是 Git 。如果你期待的是存在一个基于 Git 的工具可以识别,提问的时候应该说清楚,你的答案是 TortoiseGit 的基本操作,而非 Git 的基本操作。

    使用 merge 反复重命名的方法可以确保 Git 本身可以识别,这一点在你需要远程查看 git blame 的时候十分重要——毕竟没法要求 GitHub 远程帮你运行 TortoiseGit 。

    xiangyuecn

    87

    xiangyuecn      2025 年 5 月 23 日

    @geelaw #86 测试结果来看,git 本身是支持类似 git cp 的命令的,就是提交的时候 人为的保证复制的新文件和老文件内容一致,提交上去,外行人来看他们就自动的关联到了一起了。修改新文件再提交就没有这个效果

    跟用的 git 命令、还是 TortoiseGit 图形界面,在这个功能上是没有区别的

    cp mv 这种应该是属于同一类基本操作,要是 git 以前能明确显式的支持 cp 命令,后面这个修改复制的文件后再提交导致的不能关联的问题,也就会不存在了

    zhuisui

    88

    zhuisui      2025 年 5 月 23 日

    @xiangyuecn
    如果不是看到最后你觉得什么是对的,我一开始也不知道你到底要什么
    这反映了一个事实,你自己也不会描述你得需求。不管是对于使用 git 工具还是使用 AI 工具,清楚自己的需求知道工具的能力和限制,才能达到目的。
    是的,我就是来批判你的。你就是想不劳而获,也不想靠自己来知道工具如何工作,希望大家告诉你,然后能用就行了。
    这整篇帖子的交流,就是一个探寻用户真正需求的过程,你就是那个 xx 用户。

    zhuisui

    89

    zhuisui      2025 年 5 月 23 日

    > 跟用的 git 命令、还是 TortoiseGit 图形界面,在这个功能上是没有区别的

    > cp mv 这种应该是属于同一类基本操作,要是 git 以前能明确显式的支持 cp 命令,后面这个修改复制的文件后再提交导致的不能关联的问题,也就会不存在了

    纯粹瞎猜,显然没看我贴得几个选项的描述,所以认为这就是 git 设计出来能很容易支持的东西。
    其实这个需求 git 支持是很麻烦的,而且有很多问题的,包括可靠性和性能。
    根本就不是所谓 git 基础的能力,既高级,又需要谨慎使用,又有很多限制。

    quqiu

    90

    quqiu      2025 年 5 月 23 日

    没看懂,楼主能不能好心,重新整理一下操作说明。

    quqiu

    91

    quqiu      2025 年 5 月 23 日

    “注意不要改复制的文件就行”

    指的是不要改 a.TXT?

    xingheng

    92

    xingheng      2025 年 5 月 23 日

    Steps:
    1. mv a.txt b.txt
    2. git commit -am
    3. vim b.txt
    4. git commit -am

    第一次 commit 让 git 知道你只做了 rename ,然后历史就接上了,第二次 commit 做你认为的差异内容,历史就连上了。试试

    xiangyuecn

    93

    xiangyuecn      2025 年 5 月 23 日

    @quqiu #91 复制的新文件 b.txt 第一次提交前,不要修改内容,提交前要保持和老文件 a.txt 一致,提交后就能关联到 a.txt 的历史记录。如果修改了不一致就会关联不上,目前测试结果是这样的

    老文件 a.txt 也可以修改,修改后再复制出 b.txt ,只要提交时,两个文件内容一致,提交后就能关联上

    xiangyuecn

    94

    xiangyuecn      2025 年 5 月 23 日

    @zhuisui #88 我也不知道是哪里的问题

    “比如已有 a.txt ,我现在要个 b.txt 。 如何复制出 b.txt 这个文件,并且复制前的历史和 a.txt 保持一致?”

    这句是主题里面的原文,这段话不清楚是不是有歧义,还是需求描述不够准确,也没人提这句话有没有毛病😂

    geelaw

    95

    geelaw      2025 年 5 月 23 日

    @xiangyuecn #87 您似乎没有理解“TortoiseGit 不是 Git”这句话。

    你可以自己试试,在 Git 2.42 上

    git init
    >a.txt echo 1
    git add a.txt
    git commit -m first
    >>a.txt echo 2
    git commit -am second
    cp a.txt b.txt
    git add b.txt
    git commit -m copy
    git blame b.txt

    你会看到 b.txt 的每一行都是 copy 导致的,而不是 first, second 导致的。

    现在,为什么 TortoiseGit 会显示 b.txt 来自 a.txt (按照你的说法是这样的,我自己不用 TortoiseGit 所以不知道)。答案是因为 TortoiseGit 会自己读取 Git 的 commit 对象并自己追踪文件,和 git blame 没有任何关系。

    Git 不会认为 b.txt 和 a.txt 有相同的历史,至于 TortoiseGit 有自己的解读方式,那是 TortoiseGit 的事情。本质上来说,你自己写一个追踪复制的程序,等价于你用 TortoiseGit ,都和 Git 自带的 blame 解读没有任何关系。

    geelaw

    96

    geelaw      2025 年 5 月 23 日

    我在 #86 的主要观点有两个:

    1. 如果你希望用一个工具自动检查文件复制的情况,应该问“有什么工具能追踪 Git 历史中复制的文件”,而不是“Git 基操:怎么样复制一个文件,能保持历史记录”,因为后者的自然解读是“保持 Git 所认为的历史记录”,即 git blame 的结果。
    2. 如果你希望确保 git blame 总是得到你希望的历史(这和其他第三方工具没有任何关系),那么单纯复制 + git add + git commit 是不行的。

    wynemo

    97

    wynemo      2025 年 5 月 23 日

    这还不简单,ln -s 🤡

    fpk5

    98

    fpk5      2025 年 5 月 24 日

    这不是本来就支持的吗?不用--follow 啊
    ```
    $ git log -p -- b.txt
    commit a7d5f10cfba6f13a2eb09388bd2a941e95bfcb99 (HEAD -> main)
    Date:

    update b.txt

    diff --git a/b.txt b/b.txt
    index ce01362..3b18e51 100644
    --- a/b.txt
    +++ b/b.txt
    @@ -1 +1 @@
    -hello
    +hello world

    commit e30f877d34497b41feac93d56d77d5282f8bb257
    Date:

    add b.txt

    diff --git a/a.txt b/b.txt
    similarity index 100%
    copy from a.txt
    copy to b.txt

    commit fb7f7cf2ccbd5e488bc21a684414d4c7373f82ab
    Date:

    first commit

    diff --git a/a.txt b/a.txt
    new file mode 100644
    index 0000000..ce01362
    --- /dev/null
    +++ b/a.txt
    @@ -0,0 +1 @@
    +hello
    ```
    重点是`similarity index 100%`。

    way2create

    99

    way2create      2025 年 5 月 24 日

    @qping 这种需求可以理解,那你们应该也适用 up 的这种情况了,但说实话我是没想到 up 要的是这个,毕竟我以为他提问前就算不会完整文档看一遍,但多多少少起码有检索看看文档的,而--follow 参数就在 git log -文档 OPTIONS 中的第一个选项,列出重命名前的历史记录,所以我以为他要的是 git log 什么参数都不加就能显示一样的历史