git - 向所有分支添加/提交文件
问题描述
假设我在一个分支上并且索引很脏。我对文件 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..所以我需要在每个分支上提交 b 还是不提交?为什么不?
解决方案
所以基本上[我的循环]检查所有分支,然后检查文件x..所以我需要在每个分支b上提交还是不提交?为什么不?
答案是否定的。你几乎可以肯定确实需要一些提交。
请记住,分支名称实际上是一个指针,指向一个特定的提交。其中一个名称HEAD
附加了名称1,所有其他名称都指向某个提示提交。让我们画出这些提交和名称的图片。假设有四个名称和三个这样的分支提示提交:
T <-U <-X <--name1 (HEAD)
/
... <-V <-Y <--name2, name3
\
W <-Z <-- name4
这里的大写字母代表一些实际的提交哈希 ID。
1更准确地说,它最多HEAD
附有一个名字。如果HEAD
是detached,它直接指向某个提交。在这种情况下git rev-parse --abbrev-ref HEAD
,只会打印HEAD
. 检查这一点通常是明智的,但如果您在工作时这样做并且确定您没有处于分离的 HEAD 上,则没有必要。
您的第一步是:
current_branch="$(git rev-parse --abbrev-ref HEAD)" git add . git commit -am "added x"
您当前的分支是name1
,它指向 commit X
。第一行设置current_branch
为name1
。您当前的提交是 commit X
:此提交的文件存在于您的index和work-tree中,因为您git checkout name1
在最近的某个时间运行并且从 commit 填充了 index 和 work-tree X
。
您用于将当前目录或任何子目录中的所有git add .
文件复制到索引中, 2以便它们可以被提交。这包括您刚刚在工作树中创建的这个新文件。您的索引现在可以提交了。x
2更准确地说,这会将 (a) 已在索引中或 (b) 未被忽略的所有此类文件从工作树复制到索引中。此处的 (a) 部分由 (b) 部分暗示——根据定义,索引中的文件不会被忽略——但值得强调。
然后,第三行执行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
(我用完了罗马字母,所以这是提交 alpha。)
3在整个过程中,我们假设一切正常,或者如果命令失败,则该失败是好的。
您脚本中的下一个命令似乎在这里没有任何功能:
git fetch origin
这将获得 Gitorigin
所没有的新提交,并更新您的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)
which 省略refs/heads/
部分来解决。
在我们这里的假设示例中,您将获得的名称是name1
、name2
、name3
和name4
。因此,您将首先要求 Gitα
再次提取提交——因为它已经存在,所以执行速度非常快——然后使用名称name1
将文件提取x
到索引和工作树中。那些也已经在那里了。
建议是添加git commit
. 此特定操作git commit
会失败,并显示没有可提交的错误,在这种特殊情况下,这可能是您想要的:x
在 branch 的提示提交中已经有一个具有正确内容的文件name1
,即在 commit 中α
。
然后循环将继续git checkout name2
,即,提交Y
。这会将您的索引和工作树内容替换为从 commit 中提取的内容Y
,并附HEAD
加到 name name2
。该行将从提交中git checkout name1 -- x
提取文件到索引和工作树中,并且建议将进行新的提交,因此导致名称向前移动以指向这个新的提交。 请注意,名称继续指向 commit 。 让我们绘制新的提交,我们可以称之为(测试版):x
α
git commit
name2
name3
Y
β
U--X--α <-- name1 (HEAD)
/
T β <-- name2
/ /
...--V--Y <-- name3
\
W--Z <-- name4
现在你的循环移动到name3
,它仍然指向 commit Y
,所以 Git 会将索引和工作树设置回它们刚才的方式,当你Y
通过 name 签出提交时name2
。Git 现在将像以前一样x
从提交中提取文件,并进行另一个新的提交。α
这是事情变得非常有趣的地方!新提交与 commit 具有相同的树β
。它也有相同的作者和提交者。根据您构建消息的方式,它可能也具有与提交-m
相同的日志消息β
。如果 Git 在提交时使用的同一时间戳秒内进行此提交β
,则新提交实际上是现有提交β
并且一切都很好。
另一方面,如果 Git 花费了足够的时间使新提交获得不同的时间戳,则新提交与 commit 不同β
。让我们假设这确实发生了,并且我们得到了 commit γ
(gamma):
U--X--α <-- name1 (HEAD)
/
T β <-- name2
/ /
...--V--Y--γ <-- name3
\
W--Z <-- name4
最后,循环将再次执行相同的过程 for name4
,它当前指向提交Z
,但最终将指向新的提交δ
(增量):
U--X--α <-- name1 (HEAD)
/
T β <-- name2
/ /
...--V--Y--γ <-- name3
\
W--Z--δ <-- name4
一般问题
当多个名称指向同一个底层提交时,就会出现一个问题。在这种情况下,您必须决定是否要以相同的方式调整所有名称 - 即,让两者都提前指向提交- 或者您是否不想要它:name2
name3
β
如果你确实想要这个,你必须确保你使用一些分支名称更新操作(
git branch -f
,git merge --ff-only
等)来更新所有指向特定提交的名称。否则,您将依赖于在一秒钟内完成所有提交,以便时间戳将全部匹配。如果你不想要这个——如果你需要名字来个性化,你必须确保你
git commit
的 s 至少相隔一秒,以便它们获得唯一的时间戳。
如果你确定你所有的名字都指向不同的提交,这个问题就会消失。
其他需要考虑的事情是:
是否有任何名称指向某些具有名为 的文件的现有提交?
x
如果是这样,您将从我们从提交中提取的内容中覆盖它 (我们在当前分支上进行的第一次提交,在整个过程开始时。)x
x
α
如果确实有任何名字
x
——肯定有,那就是我们最初所在的分支——那么它与commit 中的那个x
匹配α
吗?如果是这样,git commit
除非我们添加--allow-empty
. 但在我们这里的特殊情况下,这可能是一件好事,因为这意味着我们可以避免使用特殊情况来测试是否$b
匹配$current_branch
。你真的有所有的分支名称......好吧,不清楚如何称呼这些东西。看看我们所说的“分支”到底是什么意思?详情。我们称它们为发展线。你有每个这样的行的(本地)分支名称吗?这可能就是您
git fetch origin
在这里的原因:这样您就可以累积所有origin/*
远程跟踪名称的更新。如果您有
origin/feature1
andorigin/feature2
,此时您可能想要创建(本地)分支名称feature1
andfeature2
,以便将文件添加x
到这两个分支的提示提交中。您需要分支名称来记住新创建的提交。但是仅仅使用git for-each-ref
overrefs/heads
不会达到预期的结果:您可能想要使用git for-each-ref
over ,将名称减去部分与所有名称(当然减去部分)一起refs/remotes/origin
累积到一个集合中,并将它们用作分支名称。origin/
refs/heads
refs/heads/
推荐阅读
- sql - Azure SQL - 将列添加到外部表上的查询时出现奇怪的错误
- python - 如何将对象从 Uvicorn 服务器传递到 App 实例
- apache-kafka - 更新日志主题和重新分区主题 kafka 流
- c - c 中的简单 shell - valgrind 显示排序命令的内存泄漏
- hive - 如何以演示用户的身份上传 Hue 中的文件
- bokeh - 在 JS 中嵌入散景对象。CORS 问题
- python - 从 tf.function 中 tensorflow 2.x 中任何类型的对象列表中选择项目
- python-3.x - 仅当我们在变量中有值时才在字典中创建对
- here-maps-rest - 将三词地址转换为地理坐标
- postgresql - 在 Bitnami Keycloak Helm 图表中指定 sslmode