说我在一个分支上,索引很脏。我对文件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上提交还是不提交?为什么不呢?
答案 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 -a
与git 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/name1
,refs/heads/name2
,依此类推。 git checkout
会将其中每一个作为分离的HEAD 签出,这不是您想要的。通过使用%(refname:short)
省略了refs/heads/
部分,可以轻松解决此问题。
在我们的假设示例中,您将获得的名称为name1
,name2
,name3
和name4
。因此,您将首先要求Git再次提取提交α
(因为它已经存在,所以很快),然后使用名称name1
将文件x
提取到索引中并进行工作-树。这些也已经在那里。
建议添加git commit
。这个特定的git commit
将失败,并显示一条错误消息,提示您没有要提交的内容,在这种情况下,这可能就是您想要的:已经有一个文件x
与在分支name1
的最先提交中(即,在提交α
中正确的内容。
然后循环将继续进行到git checkout name2
,即,提交Y
。这将用从提交Y
中提取的内容替换索引和工作树内容,并将HEAD
附加到名称name2
。 git 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_branch
和git 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