向所有分支添加文件/将文件提交

时间:2018-08-30 08:51:25

标签: git git-checkout

说我在一个分支上,索引很脏。我对文件x进行了更改,我也进行了其他更改。

是否可以将文件x添加到所有现有分支?像这样:

    #!/usr/bin/env bash
    current_branch="$(git rev-parse --abbrev-ref HEAD)"
    git add .
    git commit -am "added x"
    git fetch origin
    git for-each-ref --format='%(refname)' refs/heads  | while read b ; do
       git checkout "$b" 
       git checkout "$current_branch" -- x
    done
    git checkout "$current_branch";  # finally, check out the original branch again

所以基本上它签出所有分支,然后签出文件x..so,我需要在每个分支b上提交还是不提交?为什么不呢?

2 个答案:

答案 0 :(得分:1)

  

所以基本上[我的循环]签出所有分支,然后签出文件x..so,我需要在每个分支b上提交还是不提交?为什么不呢?

答案是否定的。您几乎可以肯定需要 some 提交。

请记住,分支 name 实际上是 pointer ,指向一个特定的提交。这些名称之一的名称HEAD附在 上, 1 ,所有其他名称均指向某个尖端提交。让我们画出这些提交和名称的图片。假设有四个名称,以及三个这样的分支提示提交:

  T  <-U  <-X   <--name1 (HEAD)
 /
...  <-V  <-Y   <--name2, name3
  \
   W  <-Z   <-- name4

这里的大写字母代表一些实际的提交哈希ID。


1 更准确地说,最多一个名称附加了HEAD。如果HEAD分离的,它将直接指向某个提交。在这种情况下,git rev-parse --abbrev-ref HEAD将仅打印HEAD。进行检查通常是明智的选择,尽管如果您在工作时正在执行此操作,并且可以确定自己不在独立的HEAD上,则没有必要。


您的第一步是:

current_branch="$(git rev-parse --abbrev-ref HEAD)"
git add .
git commit -am "added x"

您的当前分支name1,它指向提交X。第一行将current_branch设置为name1。您的当前提交是提交X:由于事实,此提交的文件存在于您的 index work-tree 中您在最近的某个时间运行git checkout name1,并且填充了提交X中的索引和工作树。

您使用git add .将当前目录或任何子目录中的所有文件复制到索引 2 中,以便可以提交它们。这包括您刚刚在工作树中创建的这个新文件x。您的索引现在可以提交了。


2 更准确地说,这会将所有这样的文件从工作树复制到索引中,这些文件(a)已在索引中,或(b)被忽略。 (b)部分暗示了这里的(a)部分-索引中的文件在定义上并未被忽略-但值得强调。


然后,第三行执行git commit。 (尚不清楚为什么将git commit -agit add .一起使用,但是-a会在需要的其他目录中添加文件。您可以同样地运行git add --all,假设使用Git 2.0或稍后,并省略-a。)假设它成功了, 3 现在在X之后有一个新的附加提交,name1指向这个新的提交,所以图片现在应该像这样:

  T--U--X--α   <-- name1 (HEAD)
 /
...--V--Y   <-- name2, name3
  \
   W--Z   <-- name4

(我用完了罗马字母,所以这是提交字母。)


3 在这整个过程中,我们假设一切正常,或者如果命令失败,则认为此失败很好。


您的脚本中的下一个命令似乎在这里不起作用:

git fetch origin

这将获得origin上的Git所没有的新提交,并更新origin/*的远程跟踪名称,但是在此之后您将不再使用远程跟踪名称。 ,那么为什么要在此时更新它们?

有问题的部分在循环中出现在这里

git for-each-ref --format='%(refname)' refs/heads  | while read b ; do
   git checkout "$b" 
   git checkout "$current_branch" -- x
   # proposed: git commit -m "some message"
done

首先,%(refname)输出将读取refs/heads/name1refs/heads/name2,依此类推。 git checkout会将其中每一个作为分离的HEAD 签出,这不是您想要的。通过使用%(refname:short)省略了refs/heads/部分,可以轻松解决此问题。

在我们的假设示例中,您将获得的名称为name1name2name3name4。因此,您将首先要求Git再次提取提交α(因为它已经存在,所以很快),然后使用名称name1将文件x提取到索引中并进行工作-树。这些也已经在那里。

建议添加git commit。这个特定的git commit失败,并显示一条错误消息,提示您没有要提交的内容,在这种情况下,这可能就是您想要的:已经有一个文件x与在分支name1的最先提交中(即,在提交α中正确的内容。

然后循环将继续进行到git checkout name2,即,提交Y。这将用从提交Y中提取的内容替换索引和工作树内容,并将HEAD附加到名称name2git checkout name1 -- x行将从提交x中提取文件α到索引和工作树中,提议的git commit将进行新的提交,因此导致名称{{ 1}},继续指向此新提交。 请注意,名称name2继续指向提交name3让我们绘制新的提交,我们可以将其称为Y(测试版):

β

现在,您的循环继续前进到 U--X--α <-- name1 (HEAD) / T β <-- name2 / / ...--V--Y <-- name3 \ W--Z <-- name4 ,它仍然指向提交name3,因此Git会将索引和工作树设置回刚才提交时的状态Y通过名称Y签出。 Git现在将像以前一样从提交name2中提取文件x,并进行另一个新提交。

这是非常有趣的地方!新提交与提交α tree 相同。它还具有相同的作者和提交者。根据您构造β消息的方式,它可能也具有与提交-m相同的 log消息。如果Git在进行提交β时使用的时间戳中是第二次提交,则新的提交实际上是现有提交β一切都很好。

另一方面,如果Git花费足够的时间使新提交的时间戳不同,则新提交与提交β不同。假设确实发生了这种情况,并且我们得到了提交β(伽玛):

γ

最后,循环将再次对 U--X--α <-- name1 (HEAD) / T β <-- name2 / / ...--V--Y--γ <-- name3 \ W--Z <-- name4 执行相同的过程,该循环当前指向提交name4,但最终指向新的提交Z(增量):< / p>

δ

一般问题

当多个名称指向相同基础提交时,就会出现一个问题。在这种情况下,您必须决定是否要以相同的方式调整所有名称,即让 U--X--α <-- name1 (HEAD) / T β <-- name2 / / ...--V--Y--γ <-- name3 \ W--Z--δ <-- name4 name2都前进到指向提交name3或您是否不想要

  • 如果您要这样做,您必须 确保您使用某些分支名称更新操作(β,{{ 1}}等)来更新 all 指向特定提交的名称。否则,您将依靠在一秒钟之内完成所有提交,以便所有时间戳都匹配。

  • 如果您想要此功能(如果您需要按名称进行个性化设置),则必须确保您的git branch -f至少出现一个间隔一秒,以便获得唯一的时间戳。

如果您确定所有名称都指向不同提交,则此问题将消失。

要考虑的其他事情是这些:

  • 是否有任何名称指向确实有一个名为git merge --ff-only的文件的现有提交?如果是这样,您将覆盖我们从提交git commit中提取的x中的 this x(在当前分支的第一个提交中,整个过程的开始。)

  • 如果 do 的任何名称具有x(当然有,那是我们最初所在的分支),那么{{1 }} match 提交α中的那个?如果是这样,除非我们添加x,否则建议的x将失败。但是在我们这里的特殊情况下,这可能是一件好事,因为这意味着我们可以避免使用特殊情况来测试α是否与git commit相匹配。

  • 您实际上是否为所有……都拥有分支名称...好吧,尚不清楚如何称呼这些东西。有关详情,请参见What exactly do we mean by "branch"?。我们称它们为发展线。每个这样的行都有一个(本地)分支名称吗?这可能就是为什么这里有--allow-empty的原因:这样您就可以累积所有$b远程跟踪名称的更新。

    如果您有$current_branchgit fetch origin,则此时可能要创建(本地)分支名称origin/*和{{1} },以便将文件origin/feature1添加到这两个分支的尖端提交中。您将需要分支名称来记住新创建的提交。但是仅在origin/feature2上使用feature1并不能达到预期的结果:您可能要在feature2上使用x,将减去git for-each-ref部分的名称累加到一个集合以及所有refs/heads名称(当然要减去git for-each-ref部分),并使用那些作为分支名称。

答案 1 :(得分:0)

for remote in git branch -r; do git checkout  —track $remote ; <make your changes> ;  git add . ; git commit -a -m “change commit” ; git push origin $remote ; done
相关问题