【问题标题】:Split a git branch or commit by file type按文件类型拆分 git 分支或提交
【发布时间】:2016-11-29 09:40:58
【问题描述】:

我有一个包含 html 和 javascript 代码的分支。出于横切的原因,我需要先提交对 html 的大规模更改,然后再提交 js.目前我有一个分支,其中包含两种更改。

如何按文件类型将提交分类为两个较小的提交(或分支)?

【问题讨论】:

  • 签出“clean”分支...然后创建一个名为HTML的分支...然后重新签出“clean”(因为您将签入@ 987654322@) 分支,创建一个名为JS 的分支。签出HTML 分支——只更改你的HTML ....提交..推送——然后签出你的JS分支..进行JS更改..提交——推送..然后签出“干净”分支..合并HTML——合并JS——推送“干净”分支——完成。
  • @Zak 我已经对 HTML 进行了更改,我需要将它们移动到 HTML 分支,而不是重做它们。我有数百个需要手动开发的文件。

标签: git


【解决方案1】:

我从this answer 一起破解了它:

git checkout -b new-branch # checkout from master
git diff big-branch --name-only -- '*.html' | xargs git checkout html-comments-take-two --

如果文件名中有空格,可能需要通过管道传送到 sed 's, ,\\&,g'

我无法使用 **/*.html 样式路径规范让它工作,我不知道为什么。

【讨论】:

    【解决方案2】:

    您无法更改现有的提交,但您可以进行新的提交,其父提交与现有的“提交过多”提交相同。

    在你开始之前,确保你有一个干净的工作树(“没有什么可提交的”)。这样就没有git reset 或任何可能失去任何东西的东西。如有必要,您可以进行新的提交,以便您可能需要zorg~2 而不是zorg~1(参见下图)。稍后您将能够从此提交中检索您保存的项目。

    画出你现在拥有的东西

    与 Git 一样,首先绘制(至少部分)提交图。您现在在某个分支上,这意味着您的分支 name 指向最尖端的提交,并且该提交指向某个父提交,依此类推:

    ...--A--B--C--D   <-- zorg
    

    zorg 是您当前的分支,D 可能是这个太大的提交,C 是它之前的提交,没有任何一组更改。 (如果您必须进行更多提交,那么提交D 可能会退后一步;如果是,请调整以下数字。)

    提示:使用git log --graph --oneline --decorate(也可以使用--all)让Git 为您绘制图形(尽管它是垂直绘制的,顶部是最近的东西,而不是水平方向的新东西)右边)。

    画出你喜欢的东西

    您无法更改D,但可以进行新的提交EF,您可以这样安排:

    ...--A--B--C--D     <-- ... we'll fill this in later ...
                \
                 E--F   <-- ... likewise this ...
    

    或者这样:

                 F      <-- ...
                /
    ...--A--B--C--D     <-- ...
                \
                 E      <-- ...
    

    提交 D 将继续是您的“太大”提交,而 E 可能只有 HTML 更改,F 可能只有 JS 更改。 (如果F 是基于E 构建的,那么它确实有两个变化并且实际上在内容方面匹配提交D。如果F 是基于C 构建的,那么它只有 JS 发生变化。由您决定如何安排这些。)

    每个...都要填写一个分支名称。您可以不理会现有的分支名称,并发明一两个新的分支名称,这就是我将首先展示的内容。

    手动操作

    假设您想要两个新的分支名称,EF 每个都将 C 作为它们的父级(所以,不是 C--E--F)。 Git 是 Git,有很多方法可以做到这一点,但一种简单的方法是使用 git checkout -b 创建它们,它会创建新的分支名称并打开它们(这样git status 就表示您在新分支上)。这个git checkout -b 命令还带有一个可选的提交说明符,它是创建新分支后在索引和工作树中的提交。我们希望EF 都从C 中跳出来,所以我们要创建新的分支“at”提交C

    git checkout -b zorg-html zorg~1
    

    名称zorg 标识提交D。添加~ 后缀意味着“从这个提交,后退到第一父链接,无论我在数字中说了多少次”。由于数字是 1(一),我们将退回一位父级,这会将我们从 D 带到 C。这意味着名称 zorg-html 当前将指向提交 C,我们将在这个新分支上。

    现在我们在zorg-html(在提交C)我们只想替换所有的HTML文件。这些文件的正确版本在提交D 中,正如名称zorg 所指出的那样。获取这些文件的简单但困难的方法是:

    git checkout zorg -- first_file second_file third_file ...
    

    这-这有点疯狂git checkout-这次根本不更改分支,而是只提取特定的命名文件(@987654370 之后的文件名列表@part) 来自指定的提交(zorg,即提交D)。

    如果文件都以.html结尾并且没有.html文件实际上不是HTML文件,这种简单方法的简单版本是:

    git checkout zorg -- '*.html' '**/*.html'
    

    也就是说,从顶级目录中获取每个名为 whatever.html 的文件,以及在任意数量的子目录中名为 whatever.html 的每个文件,从 zorg 提交(再次提交 D) .

    这种git checkout将更新后的文件同时写入索引和工作树,所以此时你可以简单地git commit得到结果。

    现在,要创建提交 F,我们重复整个过程:

    git checkout -b zorg-js zorg~1  # new zorg-js branch starting at C
    git checkout zorg -- '*.js' '**/*.js'
    git commit
    

    (假设和之前的 HTML 文件一样,每个 JS 文件都被命名为 .js 并且没有一个名为 .js 的文件是 other 而不是 JS 文件)。现在我们有了:

                 F      <-- zorg-js
                /
    ...--A--B--C--D     <-- zorg
                \
                 E      <-- zorg-html
    

    显然,您可以为所有这些分支选择更好的名称。

    如果您希望在提交E 之后提交F,只需省略将创建一个新分支的git checkout -b 并切换回提交C。当您提取所有.js 文件并提交F 时,这将使您留在分支zorg-html 上提交E,因此F 的父级将是E,您将拥有:

    ...--A--B--C--D     <-- zorg
                \
                 E--F   <-- zorg-html # zorg-html is clearly a bad name
    

    如果你想要的只是一些简单的食谱,你可以在这里停下来。如果您想了解许多处理此问题和其他问题的方法,请继续阅读。

    如果你想在zorg 上使用E--F 怎么办?

    没问题。 Git 是 Git,有多种方法可以做到这一点。例如,您可以在开始之前重命名zorg

    git branch -m zorg gary-oldman
    

    现在你有了这个:

    A--B--C--D   <-- gary-oldman
    

    您可以安全地创建一个新的zorg

    当然,任何上游设置都使用重命名的分支。没什么大不了的,您可以使用git branch --set-upstream-to 为每个分支设置新的上游。

    当然,Git 就是 Git,还有另一种方法可以做到!您可以创建一个新的分支名称​​现在,指向提交D,只要您需要它就记住它——您将需要它用于两个git checkout 命令。然后你可以git reset分支名zorg让它指向提交C

    git checkout zorg  # make sure zorg is the current branch
    git branch temp    # save its tip commit under a new name
    git reset --hard zorg~1  # and move zorg back to commit C
    

    现在,当您进行新提交时,他们会将名称 zorg 向前移动,但名称 temp 仍会为您记住提交 D

    A--B--C--D   <-- temp
           \
            E    <-- zorg
    

    现在要访问提交D,您将使用名称temp,并重新找到提交C,您将使用temp~1

    请注意,如果您有“过去”D 的额外提交(例如在 HTML 和 JS 更改后保存所做的工作):

    A--B--C--D--H--I--J   <-- temp, or zorg, or whatever
    

    你仍然可以做到这一切。只是现在,要命名提交 C,您将需要它的 SHA-1 哈希“真实名称”(它永远不会改变,但很难正确输入——鼠标剪切和粘贴在这里很有帮助),或从小费倒数。这里temp 可能命名为commit J,而temp~1 是commit I,而temp~2H;那么temp~3Dtemp~4C。完成拆分提交后,您可以挑选剩余的提交。

    使用git rebase -i

    Git 是 Git,还有另一种方法可以做到这一点,如果在 D 之后有提交,则特别有用,即要拆分的提交。这个特殊的方法需要对 Git 有一定的了解,但最终是最短和最快的方法。我们从git rebase -i 开始,将提交D(以及任何以后的提交)重新定位到C,它已经在(或它们在)那里;但我们将Dpick 行更改为edit

    Git 现在让我们进入 rebase 会话,并提交 D。现在我们想要git reset HEAD~1(或git reset --mixed HEAD~1--mixed 只是默认值)返回提交C。这将设置当前提交——我们处于分离 HEAD 模式,所以这只是将HEAD 本身调整为C 并重置索引以匹配C,但保留为D 设置的工作树。现在我们只是有选择地git add 我们想要的文件:所有.html 的文件。使用您喜欢的任何方法(例如find ... | xargs git addgit add '*.html' '**/*.html')添加这些,然后git commit 结果。然后git add 剩余文件和git commit 再次,然后git rebase --continue 复制剩余提交并将分支标签移动到最尖端的结果提交。

    【讨论】:

    • 我可以让 */ 深度递归吗?
    • @djechlin: **/ 已经是这个意思了。在 Git 中,*/*.js 将匹配 dir1/foo.jsdir2/bar.js,但不匹配 dir3/dir4/baz.js。但是,**/*.js 将匹配所有三个。 (例如,尝试将这些模式提供给 git ls-files。记得引用它们以便它们传递给 Git;否则大多数 shell 坚持为您扩展它们。)
    猜你喜欢
    • 2020-05-07
    • 1970-01-01
    • 2017-09-11
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    • 2016-01-05
    • 1970-01-01
    相关资源
    最近更新 更多