在脚本中拉取某些 git 分支
Pulling certain git branches in a script
我有一个这样开始的脚本:
#!/bin/sh
for b in `git branch -r | grep -v -- '->'`; do git branch --track ${b##origin/} $b; done
git fetch --all
这将获取所有远程分支。我只想获取以单词 "hotfix".
开头的分支
我该怎么做?
编辑:
一开始,我也想删除所有的分支,除了 master
这与您认为的不同。我将向后解决这个问题,因为....
项目 #2:git fetch --all
表示从所有 远程 获取。这与分支关系不大。
项目 #1:让我们定义术语 远程,因为 Git 做得不是很好。 (The gitglossary
documentation 描述了一个 远程存储库 和一个 remote-tracking 分支 而没有定义 "remote" 这个词!) remote 主要只是 URL 的一个简短的 one-word 名称。经典的遥控器就是origin
这个词。大多数存储库都是通过克隆创建的,克隆会设置一个远程来保存原始 URL。此遥控器的默认名称是 origin
.
因此,git fetch --all
从所有遥控器中获取。除非您有多个遥控器,否则这没什么特别的。如果您只有 origin
,您仍然只是像往常一样从 origin
获取数据。
第 3 项是 Git 实际获取的内容的问题。在这里,事情变得复杂了。不过,让我们首先注意,每个提交都有自己的唯一 ID,即 Git 打印的那些又大又丑的 SHA-1 哈希 ID(通常缩写为 face0ff
或 cafedad
或其他) .
理解 Git 的关键之一是认识到分支 names 在 Git 中的重要性相当小。它们主要以两种方式起作用,其中一种与 git fetch
相关,所以我们很快就会回到这个问题——但首先,我们需要查看 提交图 (DAG,在 gitglossary 中)。 DAG,或 Directed Acyclic Graph,由存储库中的所有提交组成. git fetch
获取的正是这个 图表,名称只是在图表 中开始的方式。名称,如 master
或 branch1
,转换为提交 ID。 Git 通过 ID 查找提交,然后得到 Git 提交的内容。
每个提交都在其内容中存储其 parent 提交的 ID。大多数提交只有一个 parent。 Merge 提交是那些有多个 parent 的提交,并且至少有一个 root 提交,其中有 没有parents。存储库中的第一个提交必然是根提交。这意味着我们总是可以从最近的提交之一开始,然后向后工作,跟随该提交到它的 parent。使用那个 parent,我们找到它的 parents,并使用那些 parents,我们找到更多的 parents,直到我们最终回到根。如果我们画出这个过程,我们会得到这样的东西,如果没有分支和合并:
o <- o <- o ... <- o <- o <-- most-recent
其中每个 o
代表一次提交,backwards-pointing 箭头从提交指向 parent。因为它们只指向后方,不指向前方,所以我们只能从child到parent,而不能从parent到child。1
内部链接的向后方向通常不是那么重要,除了它们显示为什么我们必须有一个名字——比如分支名称—让我们开始:最近的 提交没有以后的提交指向它。这就是分支名称的用武之地,在 Git 中:分支名称是我们查找最新提交的方式。
因此,我们可能会像这样绘制 DAG,如果有三个名称 master
和两个 branch
-es,并且没有可见的合并:
o--o--o--...--o--o <-- master
\
o--...--o <-- branch1
\
o <-- branch2
在此图中,较新的提交向右,较旧的向左。还值得指出的是 root 提交实际上在 所有三个 分支上,并且在这个特定的图中,除了其中一个提交之外在 branch2
也在 branch1
。这是理解 Git 的另一个关键:提交通常同时在许多分支上。 分支名称只是让我们开始,这样我们就不会错过任何提交.它不一定是 only 方式来提交,但我们需要 some 名称,通过它我们可以找到 每次提交。2
简而言之,这些名称——分支名称、标签名称或任何其他名称——使一些提交集可达。
这相当长的一段话终于把我们带到了 git fetch
实际获取的内容。
1它 可以 "go backwards",但只能通过对整个存储库进行详尽搜索,这需要很长时间。例如,维护命令 git fsck
就是这样做的。它会找到没有指向它们的名称的提交。这些被称为 "unreferenced" 和 "dangling" 提交,它们实际上是正常的,因为 Git 在存储库的正常工作过程中分拆了许多故意放弃的提交。 Git 的 "garbage collector",或 git gc
,最终会清理这些。
2该名称不必是分支名称。例如,任何标签名称,或 refs/stash
,也可以命名提交,并且这些定位的提交根本不需要在任何分支上。
git fetch
将一些按名称定位的提交带入我们的存储库
请记住,当我们 运行 git fetch
时,我们正在让我们的 Git 联系另一个 Git。另一个 Git 有自己的、独立的、独立的 Git 存储库,有自己的提交和自己的分支。
当我们的 Git 调用他们的 Git 时,我们通常不希望我们的 Git 的提交被遗忘和 替换 及其 Git 的提交。我们通常不希望 我们的 分支被丢弃以支持 他们的 分支。3 相反,我们通常想要的是我们Git的提交得到added-to。我们希望 他们的 提交 添加到我们的 ,我们希望我们的 Git 记住他们的 Git 的分支,但是以其他名字.
这里是远程名称re-enters图片所在的地方。他们的分支有名称,如 master
和 branch1
和 hotfix
。我们的 Git 将接受他们的提交,他们的 Git 通过他们的名字找到(可以访问),并将它们与我们现有的提交结合起来。但是我们的 Git 必须在 我们的 存储库中给出他们的提交名称,这里我们的 Git 使用我们的 remote-tracking 分支名称.
当我们 运行 git fetch
时,我们的 Git 调用他们的 Git 并询问他们有什么分支(以及标签和其他名称),以及什么提交去那些名字。我们的 Git 然后检查我们是否有这些提交。如果没有,我们的 Git 会请求这些提交,以及他们的 parent 和那些 parent 的 parent 等等,直到我们的 Git找到我们已经拥有的一些提交。在这一点上,我们的 Git 不再需要他们的任何提交,因为我们刚刚找到了他们的图与我们的图连接的位置。
接下来,我们的 Git 将那些获取的提交存储在 我们的 存储库中,现在是最后的关键步骤:我们的 Git 将 ID 存储在我们的 remote-tracking 分支名称下。 也就是说,他们的 master
可能是 deadcab
而我们的是 badbeef
。我们不想取代我们的,但我们确实想记住他们的 - 所以我们 Git 记住 origin/master = deadcab
。现在我们的图表看起来像这样:
...--o--o--o <-- master (badbeef)
\
o--o <-- origin/master (deadcab)
提交deadcab
,他们的master
,指向提交cafeb0b
,后者指向badbeef
,也就是我们的master
。我们将他们的 master
称为我们的 origin/master
以使其与我们的 master
.
分开
如果我们决定喜欢他们的两个新提交,我们可以将我们的master
直接指向deadcab
:
...--o--o--o
\
o--o <-- master, origin/master (deadcab)
现在我们有两个名称指向 相同的 提交,deadcab
;但这很好。这两个名字是我们的 master
和我们的 origin/master
(我们的 origin/master
是我们的 Git 对 他们的 master
基于我们上次从他们那里获取的时间)。
3如果我们做想要那个,这叫做"fetch mirror",git fetch
可以实现这个直接这几乎是您想要的,但不完全是。
你想要的几乎是,但不完全是一个获取镜像
您建议您想要的是:
删除所有本地分支名称,master
除外。这是一件有效的事情,但要小心,因为它会使您自己的提交无法访问。您拥有的任何其他人没有的提交,仅通过您自己的本地分支名称name-able,不再是name-able。这将使他们有资格进行垃圾收集。
获取(作为remote-tracking分支)他们正在调用的分支hotfix*
,并使本地分支指向相同的提交。
在脚本中执行此类工作的 Git 命令是 git for-each-ref
。要使用它,您需要知道您自己的本地分支机构是一种特定的 Git reference(因此 for-each-ref
)。引用只是一个以 refs/
开头的名称,而 分支名称 只是一个以 refs/heads/
开头的引用。 remote-tracking 分支 只是一个以 refs/remotes/
开头然后具有远程名称的引用,因此所有 origin
都是 refs/remotes/origin/
.
因此,我们要分三步完成:
git fetch origin
:调用存储在 origin
下 URL 的 Git,从中获取任何新的提交,并更新我们自己的origin/*
remote-tracking 个分支(即 refs/remotes/origin/
中的所有内容)。我们可能也应该使用 --prune
,它告诉我们的 Git 从我们的 remote-tracking 分支中 删除 ,任何 origin/*
分支origin
上不再存在。因此:
git fetch --prune origin
git for-each-ref refs/heads
:这会让我们对每个本地分支做一些事情。我们要删除它,除非它的名字是 master
。这也需要一点小心,因为我们不能删除我们有 checked-out 的分支,所以先 git checkout master
可能是个好主意
git checkout master
git for-each-ref --format='%(refname:short)' refs/heads | while read b; do
[ $b == master ] || git branch -D $b
done
创建名称模仿 remote-tracking 名称与 hotfix*
:
形式匹配的分支的新本地分支
git for-each-ref --format='%(refname:short)' 'refs/remotes/origin/hotfix*' |
while read rb; do
b=${rb#origin/}
git branch $b --track $rb
done
我有一个这样开始的脚本:
#!/bin/sh
for b in `git branch -r | grep -v -- '->'`; do git branch --track ${b##origin/} $b; done
git fetch --all
这将获取所有远程分支。我只想获取以单词 "hotfix".
开头的分支我该怎么做?
编辑: 一开始,我也想删除所有的分支,除了 master
这与您认为的不同。我将向后解决这个问题,因为....
项目 #2:git fetch --all
表示从所有 远程 获取。这与分支关系不大。
项目 #1:让我们定义术语 远程,因为 Git 做得不是很好。 (The gitglossary
documentation 描述了一个 远程存储库 和一个 remote-tracking 分支 而没有定义 "remote" 这个词!) remote 主要只是 URL 的一个简短的 one-word 名称。经典的遥控器就是origin
这个词。大多数存储库都是通过克隆创建的,克隆会设置一个远程来保存原始 URL。此遥控器的默认名称是 origin
.
因此,git fetch --all
从所有遥控器中获取。除非您有多个遥控器,否则这没什么特别的。如果您只有 origin
,您仍然只是像往常一样从 origin
获取数据。
第 3 项是 Git 实际获取的内容的问题。在这里,事情变得复杂了。不过,让我们首先注意,每个提交都有自己的唯一 ID,即 Git 打印的那些又大又丑的 SHA-1 哈希 ID(通常缩写为 face0ff
或 cafedad
或其他) .
理解 Git 的关键之一是认识到分支 names 在 Git 中的重要性相当小。它们主要以两种方式起作用,其中一种与 git fetch
相关,所以我们很快就会回到这个问题——但首先,我们需要查看 提交图 (DAG,在 gitglossary 中)。 DAG,或 Directed Acyclic Graph,由存储库中的所有提交组成. git fetch
获取的正是这个 图表,名称只是在图表 中开始的方式。名称,如 master
或 branch1
,转换为提交 ID。 Git 通过 ID 查找提交,然后得到 Git 提交的内容。
每个提交都在其内容中存储其 parent 提交的 ID。大多数提交只有一个 parent。 Merge 提交是那些有多个 parent 的提交,并且至少有一个 root 提交,其中有 没有parents。存储库中的第一个提交必然是根提交。这意味着我们总是可以从最近的提交之一开始,然后向后工作,跟随该提交到它的 parent。使用那个 parent,我们找到它的 parents,并使用那些 parents,我们找到更多的 parents,直到我们最终回到根。如果我们画出这个过程,我们会得到这样的东西,如果没有分支和合并:
o <- o <- o ... <- o <- o <-- most-recent
其中每个 o
代表一次提交,backwards-pointing 箭头从提交指向 parent。因为它们只指向后方,不指向前方,所以我们只能从child到parent,而不能从parent到child。1
内部链接的向后方向通常不是那么重要,除了它们显示为什么我们必须有一个名字——比如分支名称—让我们开始:最近的 提交没有以后的提交指向它。这就是分支名称的用武之地,在 Git 中:分支名称是我们查找最新提交的方式。
因此,我们可能会像这样绘制 DAG,如果有三个名称 master
和两个 branch
-es,并且没有可见的合并:
o--o--o--...--o--o <-- master
\
o--...--o <-- branch1
\
o <-- branch2
在此图中,较新的提交向右,较旧的向左。还值得指出的是 root 提交实际上在 所有三个 分支上,并且在这个特定的图中,除了其中一个提交之外在 branch2
也在 branch1
。这是理解 Git 的另一个关键:提交通常同时在许多分支上。 分支名称只是让我们开始,这样我们就不会错过任何提交.它不一定是 only 方式来提交,但我们需要 some 名称,通过它我们可以找到 每次提交。2
简而言之,这些名称——分支名称、标签名称或任何其他名称——使一些提交集可达。
这相当长的一段话终于把我们带到了 git fetch
实际获取的内容。
1它 可以 "go backwards",但只能通过对整个存储库进行详尽搜索,这需要很长时间。例如,维护命令 git fsck
就是这样做的。它会找到没有指向它们的名称的提交。这些被称为 "unreferenced" 和 "dangling" 提交,它们实际上是正常的,因为 Git 在存储库的正常工作过程中分拆了许多故意放弃的提交。 Git 的 "garbage collector",或 git gc
,最终会清理这些。
2该名称不必是分支名称。例如,任何标签名称,或 refs/stash
,也可以命名提交,并且这些定位的提交根本不需要在任何分支上。
git fetch
将一些按名称定位的提交带入我们的存储库
请记住,当我们 运行 git fetch
时,我们正在让我们的 Git 联系另一个 Git。另一个 Git 有自己的、独立的、独立的 Git 存储库,有自己的提交和自己的分支。
当我们的 Git 调用他们的 Git 时,我们通常不希望我们的 Git 的提交被遗忘和 替换 及其 Git 的提交。我们通常不希望 我们的 分支被丢弃以支持 他们的 分支。3 相反,我们通常想要的是我们Git的提交得到added-to。我们希望 他们的 提交 添加到我们的 ,我们希望我们的 Git 记住他们的 Git 的分支,但是以其他名字.
这里是远程名称re-enters图片所在的地方。他们的分支有名称,如 master
和 branch1
和 hotfix
。我们的 Git 将接受他们的提交,他们的 Git 通过他们的名字找到(可以访问),并将它们与我们现有的提交结合起来。但是我们的 Git 必须在 我们的 存储库中给出他们的提交名称,这里我们的 Git 使用我们的 remote-tracking 分支名称.
当我们 运行 git fetch
时,我们的 Git 调用他们的 Git 并询问他们有什么分支(以及标签和其他名称),以及什么提交去那些名字。我们的 Git 然后检查我们是否有这些提交。如果没有,我们的 Git 会请求这些提交,以及他们的 parent 和那些 parent 的 parent 等等,直到我们的 Git找到我们已经拥有的一些提交。在这一点上,我们的 Git 不再需要他们的任何提交,因为我们刚刚找到了他们的图与我们的图连接的位置。
接下来,我们的 Git 将那些获取的提交存储在 我们的 存储库中,现在是最后的关键步骤:我们的 Git 将 ID 存储在我们的 remote-tracking 分支名称下。 也就是说,他们的 master
可能是 deadcab
而我们的是 badbeef
。我们不想取代我们的,但我们确实想记住他们的 - 所以我们 Git 记住 origin/master = deadcab
。现在我们的图表看起来像这样:
...--o--o--o <-- master (badbeef)
\
o--o <-- origin/master (deadcab)
提交deadcab
,他们的master
,指向提交cafeb0b
,后者指向badbeef
,也就是我们的master
。我们将他们的 master
称为我们的 origin/master
以使其与我们的 master
.
如果我们决定喜欢他们的两个新提交,我们可以将我们的master
直接指向deadcab
:
...--o--o--o
\
o--o <-- master, origin/master (deadcab)
现在我们有两个名称指向 相同的 提交,deadcab
;但这很好。这两个名字是我们的 master
和我们的 origin/master
(我们的 origin/master
是我们的 Git 对 他们的 master
基于我们上次从他们那里获取的时间)。
3如果我们做想要那个,这叫做"fetch mirror",git fetch
可以实现这个直接这几乎是您想要的,但不完全是。
你想要的几乎是,但不完全是一个获取镜像
您建议您想要的是:
删除所有本地分支名称,
master
除外。这是一件有效的事情,但要小心,因为它会使您自己的提交无法访问。您拥有的任何其他人没有的提交,仅通过您自己的本地分支名称name-able,不再是name-able。这将使他们有资格进行垃圾收集。获取(作为remote-tracking分支)他们正在调用的分支
hotfix*
,并使本地分支指向相同的提交。
在脚本中执行此类工作的 Git 命令是 git for-each-ref
。要使用它,您需要知道您自己的本地分支机构是一种特定的 Git reference(因此 for-each-ref
)。引用只是一个以 refs/
开头的名称,而 分支名称 只是一个以 refs/heads/
开头的引用。 remote-tracking 分支 只是一个以 refs/remotes/
开头然后具有远程名称的引用,因此所有 origin
都是 refs/remotes/origin/
.
因此,我们要分三步完成:
git fetch origin
:调用存储在origin
下 URL 的 Git,从中获取任何新的提交,并更新我们自己的origin/*
remote-tracking 个分支(即refs/remotes/origin/
中的所有内容)。我们可能也应该使用--prune
,它告诉我们的 Git 从我们的 remote-tracking 分支中 删除 ,任何origin/*
分支origin
上不再存在。因此:git fetch --prune origin
git for-each-ref refs/heads
:这会让我们对每个本地分支做一些事情。我们要删除它,除非它的名字是master
。这也需要一点小心,因为我们不能删除我们有 checked-out 的分支,所以先git checkout master
可能是个好主意git checkout master git for-each-ref --format='%(refname:short)' refs/heads | while read b; do [ $b == master ] || git branch -D $b done
创建名称模仿 remote-tracking 名称与
形式匹配的分支的新本地分支hotfix*
:git for-each-ref --format='%(refname:short)' 'refs/remotes/origin/hotfix*' | while read rb; do b=${rb#origin/} git branch $b --track $rb done