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

问题描述 投票:0回答:1

假设我在分支上并且索引很脏。我对文件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或否?为什么不?

git git-checkout
1个回答
1
投票

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

答案是否定和是。你几乎肯定需要一些提交。

请记住,分支名称实际上是指向一个特定提交的指针。其中一个名称附加了名称HEAD,1和所有其他名称指向一些提示。让我们画出这些提交和名称的图片。假设有四个名称和三个这样的分支提示:

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

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


准确地说,最多一个名字附有HEAD。如果HEAD被分离,它直接指向某个提交。在这种情况下,git rev-parse --abbrev-ref HEAD将打印HEAD。检查这个通常是明智的,但如果你在工作时这样做,并且确定你不是在一个独立的头上,那就没有必要了。


你的第一步是:

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

你当前的分支是name1,它指向提交X。第一行将current_branch设置为name1。你当前的提交是提交X:这个提交的文件存在于你的索引和工作树中,因为你在最近的某个时刻运行了git checkout name1并且从提交中填充了索引和工作树X

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


2更确切地说,这将从工作树复制到索引中,所有这些文件都是(a)已经在索引中,或者(b)不被忽略。这里的(a)部分是由(b)部分暗示的 - 根据定义,索引中的文件不被忽略 - 但值得强调。


然后,第三行是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

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


在整个过程中,我们假设一切正常,或者如果命令失败,那么这种失败是好的。


脚本中的下一个命令似乎没有任何功能:

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将进行新的提交,因此导致名称name2向前移动以指向此新提交。请注意,名称name3继续指向提交Y。让我们在新的提交中绘制,我们可以称之为β(beta):

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

现在你的循环移动到name3,它仍指向提交Y,所以Git会将索引和工作树设置回原来的状态,当你通过名称Y提交name2时。 Git现在将像之前一样从commit x中提取文件α,并再次进行新的提交。

这是事情变得非常有趣的地方!新提交与提交β具有相同的树。它也有相同的作者和提交者。根据您构建-m消息的方式,它可能与commit β具有相同的日志消息。如果Git在提交β时使用的时间戳与第二次相同,那么新提交实际上是现有的提交β,一切都很好。

另一方面,如果Git花费足够的时间使新提交获得不同的时间戳,则新提交与提交β不同。让我们假设这确实发生了,我们得到提交γ(gamma):

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

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

name2

The general problems

当多个名称指向相同的基础提交时,会出现一个问题。在这种情况下,您必须决定是否要以相同的方式调整所有名称 - 即,让name3β前进指向提交git branch -f - 或者您是否不想要它:

  • 如果您确实需要这个,则必须确保使用某些分支名称更新操作(git merge --ff-onlygit commit等)来更新指向特定提交的所有名称。否则,您将依赖于在一秒钟内完成所有提交,以便时间戳全部匹配。
  • 如果您不想要这个 - 如果您需要个性化的名称,那么您必须确保您的xs至少间隔一秒,以便他们获得独特的时间戳。

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

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

  • 是否有任何名称指向一些名为x的文件的现有提交?如果是这样,你将从我们从提交x(我们在当前分支上,在整个过程开始时进行的第一次提交)中提取的α中覆盖此x。)
  • 如果有任何名称确实有x-one肯定有,那就是我们首先处于分支的那个 - 那么α是否与提交git commit中的那个相匹配?如果是这样,除非我们添加--allow-empty,否则建议的$b将失败。但在我们这里的特殊情况下,这可能是一件好事,因为这意味着我们可以避免有一个特殊情况来测试$current_branch是否匹配What exactly do we mean by "branch"?
  • 你真的有所有的分支名称......好吧,不清楚这些东西叫什么。有关详细信息,请参阅git fetch origin。我们称之为发展线。你有每个这样的线的(本地)分支名称吗?这可能就是为什么你在这里有origin/*:这样你就可以累积所有origin/feature1远程跟踪名称的更新。 如果你有origin/feature2feature1,你可能在这一点上想要创建(本地)分支名称feature2x,以便将文件git for-each-ref添加到这两个分支的提示中。您需要分支名称来记住新创建的提交。但是仅仅使用refs/heads而不是git for-each-ref将无法实现预期的结果:你可能想要使用refs/remotes/origin而不是origin/,将名称减去refs/heads部分与所有refs/heads/名称(当然减去qazxswpoi部分)一起累积,并使用那些作为分支名称。
© www.soinside.com 2019 - 2024. All rights reserved.