Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/git/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
保存历史的Git拷贝文件_Git_File_Copy_Filenames - Fatal编程技术网

保存历史的Git拷贝文件

保存历史的Git拷贝文件,git,file,copy,filenames,Git,File,Copy,Filenames,我在Git中有一个有点令人困惑的问题。 比如说,我提交了一个文件dir1/a.txtcommitted,git保留了提交的历史记录 现在我需要将文件复制到dir2/A.txt(不是移动,而是复制)。 我知道有一个git mv命令,但我需要dir2/a.txt具有与dir1/a.txt相同的提交历史,并且dir1/a.txt仍保留在那里 我不打算在创建副本后更新A.txt,所有未来的工作都将在dir2/A.txt 我知道这听起来很混乱,我要补充的是,这种情况是在基于java的模块(mavenize

我在Git中有一个有点令人困惑的问题。 比如说,我提交了一个文件
dir1/a.txt
committed,git保留了提交的历史记录

现在我需要将文件复制到
dir2/A.txt
(不是移动,而是复制)。 我知道有一个
git mv
命令,但我需要
dir2/a.txt
具有与
dir1/a.txt
相同的提交历史,并且
dir1/a.txt
仍保留在那里

我不打算在创建副本后更新
A.txt
,所有未来的工作都将在
dir2/A.txt

我知道这听起来很混乱,我要补充的是,这种情况是在基于java的模块(mavenized项目)上发生的,我们需要创建一个新版本的代码,以便我们的客户能够在运行时拥有两个不同的版本,第一个版本最终将在对齐完成时删除。
当然,我们可以使用maven版本控制,我只是Git的新手,很好奇Git在这里能提供什么。

与subversion不同,Git没有每个文件的历史记录。如果查看提交数据结构,它只指向以前的提交和此提交的新树对象。提交对象中未存储由提交更改的文件的明确信息;也不知道这些变化的性质

检查更改的工具可以基于启发式检测重命名。例如,“git diff”有一个选项-M,用于启用重命名检测。因此,在重命名的情况下,“git-diff”可能会显示一个文件已被删除,另一个文件已创建,而“git-diff-M”实际上会检测到移动并相应地显示更改(有关详细信息,请参阅“man-git-diff”)


因此,在git中,这不是您如何提交更改的问题,而是您以后如何看待提交的更改。

为了完整起见,我想补充一点,如果您想复制一个包含受控和非受控文件的整个目录,您可以使用以下方法:

git mv old new
git checkout HEAD old
不受控制的文件将被复制,因此您应该清理它们:

git clean -fdx new

在我的例子中,我在硬盘上进行了更改(将大约200个文件夹/文件从我工作副本的一个路径剪切/粘贴到我工作副本的另一个路径),并使用SourceTree(2.0.20.1)来暂存检测到的更改(一次添加,一次删除),只要我同时暂存添加和删除,它会自动组合成一个带有粉红色R图标的单一更改(我假设重命名)

我确实注意到,因为我一次进行了大量更改,SourceTree在检测所有更改时有点慢,所以我的一些暂存文件看起来只是添加(绿色加号)或删除(红色减号),但我不断刷新文件状态,并在新更改最终弹出时不断暂存,几分钟后,整个清单都很完美,可以提交了

我验证了历史记录是否存在,只要在查找历史记录时选中“遵循重命名文件”选项即可。

您只需执行以下操作:

  • 将文件移动到两个不同的位置
  • 合并执行上述操作的两个提交,以及
  • 将一份副本移回原始位置
  • 您将能够看到这两个文件的历史属性(使用
    git日志
    )和完整的更改历史(使用
    git日志

    假设要创建名为
    bar
    的文件
    foo
    的副本。在这种情况下,您将使用的工作流如下所示:

    ( revision history )            ( files )
    
        ORIG_HEAD                      foo
         /     \                      /   \
    SAVED       ALTERNATE          bar     copy
         \     /                      \   /
          MERGED                     bar,copy
            |                           |
         RESTORED                    bar,foo
    
    git mv foo-bar
    git提交
    SAVED=`git rev parse HEAD`
    git重置--硬头^
    git mv foo拷贝
    git提交
    git merge$SAVED#这将产生冲突
    git commit-a#像这样简单地解决
    git mv copy foo
    git提交
    
    为什么会这样 执行上述命令后,将生成如下所示的修订历史记录:

    ( revision history )            ( files )
    
        ORIG_HEAD                      foo
         /     \                      /   \
    SAVED       ALTERNATE          bar     copy
         \     /                      \   /
          MERGED                     bar,copy
            |                           |
         RESTORED                    bar,foo
    
    当您向Git询问
    foo
    的历史时,它会:

  • 检测合并和还原之间从
    复制
    的重命名
  • 检测
    copy
    是否来自合并的备用父级,以及
  • 从原始头和备用头之间的
    foo
    检测重命名
  • 从那里,它将深入挖掘
    foo
    的历史

    当您向Git询问
    条形码的历史时,它会:

  • 请注意,合并和恢复之间没有更改
  • 检测
    是否来自合并的已保存父项,以及
  • 检测原始头和保存头之间从
    foo
    的重命名
  • 从那里,它将深入挖掘
    foo
    的历史

    就这么简单。:)


    您只需强制Git进入合并状态,在合并状态下,您可以接受文件的两个可跟踪副本,我们通过平行移动原始副本(我们很快会恢复)来实现这一点。

    只需复制文件,添加并提交:

    cp dir1/A.txt dir2/A.txt
    git add dir2/A.txt
    git commit -m "Duplicated file from dir1/ to dir2/"
    
    然后,以下命令将显示完整的预复制历史记录:

    git log --follow dir2/A.txt
    
    要查看从原始文件继承的逐行注释,请使用以下命令:

    git blame -C -C -C dir2/A.txt
    
    Git不会在提交时跟踪副本,而是在检查历史记录时检测副本,例如使用
    Git-dull
    Git-log

    这些信息大部分来自这里的答案:

    我稍微修改了一下,创建了一个可重用的、非交互式的shell脚本,名为
    git split.sh

    #!/bin/sh
    
    if [[ $# -ne 2 ]] ; then
      echo "Usage: git-split.sh original copy"
      exit 0
    fi
    
    git mv "$1" "$2"
    git commit -n -m "Split history $1 to $2 - rename file to target-name"
    REV=`git rev-parse HEAD`
    git reset --hard HEAD^
    git mv "$1" temp
    git commit -n -m "Split history $1 to $2 - rename source-file to temp"
    git merge $REV
    git commit -a -n -m "Split history $1 to $2 - resolve conflict and keep both files"
    git mv temp "$1"
    git commit -n -m "Split history $1 to $2 - restore name of source-file"
    

    这一过程保留了历史,但没有什么效果:

    # make branchs to new files
    $: git mv arquivos && git commit
    
    # in original branch, remove original files
    $: git rm arquivos && git commit
    
    # do merge and fix conflicts
    $: git merge branch-copia-arquivos
    
    # back to original branch and revert commit removing files
    $: git revert commit
    

    我在上的可复制示例显示,
    git-dull
    也知道重命名历史-不使用任何选项。这难道不告诉我们历史是以某种方式存储的吗?@danielder No.Like
    git diff-M
    这只是对树对象的智能分析。在
    git-dull
    手册页中:“整个文件重命名过程中自动跟随行的原点(目前没有关闭以下重命名的选项)。“那么为什么
    git-mv
    存在?@skirsch-convenciency和Mercurial不同。Mercurial有保存历史的副本。尽我所能