Git 单独文件夹中每个分支的 git worktree - Bash
Git get worktree for every branch in seperate folders - Bash
我需要编写一个 bash 脚本,每小时将我们的存储库分支复制到本地 linux 网络服务器。
我们有一个 git 远程存储库 (gitolite),其分支名为 "master" "testing" "feature-featurename" "hotfix-number"
这些分支中的每一个都应该将其工作树复制到 /var/www/html/branchname
首先:如何将不同的工作树复制到不同的文件夹中?
第二:如果 "feature-" 和 "hotfix-" 分支不断更改名称,我如何自动执行此过程?
这与 git 挂钩无关,这应该是另一个服务器上的脚本 运行,由 cron 作业触发。
可怕的几行:
mkdir -p /var/www/html
git clone --bare user@git-server:/your-repo.git && cd your-repo.git
git for-each-ref --format='%(refname)' refs/heads/ | grep 'branch-pattern' | while read branchRef; do
branchName=${branchRef#refs/heads/}
git archive --format=tar --prefix="$branchName/" "$branchRef" | tar -C/var/www/html -x
done
让我们分解一下:
确保 target 目录存在。可能你不需要。
mkdir -p /var/www/html
克隆git存储库并进入目录
git clone --bare user@git-server:/your-repo.git
列出分支。这将是来自克隆目录的 运行。请注意,未使用 git branch
,因为在脚本中使用时可能会产生令人惊讶的输出。
git for-each-ref --format='%(refname)' refs/heads/
过滤你想要的分支。在您的情况下,模式可能类似于 grep -E "(master)|(testing)|(feature-.*)"
等
grep 'branch-pattern'
while
语句读取每个分支名称并将其分配给branch
变量
- 创建一个
branchName
变量,它是不包括 ref 前缀的分支名称。请注意,这是 bash 特定的。
git archive
创建所选分支的 tar 存档,在所有条目前加上分支名称。存档被发送到标准输出
git archive --format=tar --prefix="$branch/" "$branch"
立即将存档提取到其tar获取位置
tar -C/var/www/html -x
所以首先你需要一个分支列表。出于编写脚本的目的,最适合执行此操作的命令是 for-each-ref
。假设您只想要本地分支名称,请使用类似
git for-each-ref refs/heads/* |cut -d\/ -f3
顺便说一句,上面命令中的几件事假设您不在 "namespaces" 中使用分支。如果您使用像 qa/feature-1
这样的分支名称 - 包含 /
- 那么这会改变一些事情。上面的命令就变成了
git for-each-ref refs/heads |cut -d\/ -f3-
但更大的问题是您可能必须更多地考虑分支名称应如何映射到目录名称。所以现在我将继续假设分支名称不包含 /
.
你需要处理每个分支,所以
git for-each-ref refs/heads/* |cut -d\/ -f3 |while read branch; do
# ... will process each branch here
done
现在您可以使用 git worktree
来简化个人结帐。 (请注意,与使用 archive
复制每个分支的整个提交内容,然后调用 tar
撤消您没有执行的工作相比,这应该 多 更有效一开始不想 archive
做。)
确保定义所有必需的工作树
git for-each-ref refs/heads/* |cut -d\/ -f3 |while read branch; do
if [ ! -d .git/worktrees/$branch ]; then
git worktree add /var/www/html/$branch $branch
fi
done
现在关于这一点的一件事是,当分支被移动时(即当收到推送时),它会放置工作树 "out of sync" 这样你似乎已经上演了每个 "undoing"改变推动了。 (默认工作树的保护似乎不适用。)
但这似乎符合您的要求;另一种方法是在推送进来时更新目录,您在问题描述中拒绝了。因此,在这种情况下,您的脚本应该将工作树同步到 "un-undoing" 他们
的新更改
git for-each-ref refs/heads/* |cut -d\/ -f3 |while read branch; do
if [ ! -d .git/worktrees/$branch ]; then
git worktree add /var/www/html/$branch $branch
fi
git reset --hard HEAD
done
当然有时分支会消失;如果你不想要陈旧的工作树元数据,你可以添加一个
git worktree prune
您还可以使用git worktree list --porcelain
而不是直接搜索工作树目录——这在奇怪的情况下可能更可取,例如(再次)命名空间分支。
How do I get different worktrees copied into different folders?
这是通过 git 工作树完成的,如 Mark Adelsberger's 所示。
答案以:
结尾
You also could use git worktree list --porcelain
instead of searching for worktree directories directly - and that may be preferable in odd cases like (again) namespaced branches.
Git2.36(2022 年第 2 季度,见末尾):
git worktree list --porcelain -z
实际上,您应该,尤其是 Git 2.31(2021 年第一季度):git worktree list
(man) 现在将工作树注释为可修剪,在 [=18= 中显示锁定和可修剪属性]模式,并获得了--verbose
选项。
参见 commit 076b444, commit 9b19a58, commit 862c723, commit 47409e7 (27 Jan 2021), and commit eb36135, commit fc0c7d5, commit a29a8b7 (19 Jan 2021) by Rafael Silva (raffs
)。
(由 Junio C Hamano -- gitster
-- in commit 02fb216 合并,2021 年 2 月 10 日)
worktree
: teach list
verbose mode
Helped-by: Eric Sunshine
Signed-off-by: Rafael Silva
Reviewed-by: Eric Sunshine
"git worktree list
"(man) annotates each worktree according to its state such as prunable or locked, however it is not immediately obvious why these worktrees are being annotated.
For prunable worktrees a reason is available that is returned by should_prune_worktree()
and for locked worktrees a reason might be available provided by the user via lock
command.
Let's teach "git worktree list
" a --verbose
mode that outputs the reason why the worktrees are being annotated.
The reason is a text that can take virtually any size and appending the text on the default columned format will make it difficult to extend the command with other annotations and not fit nicely on the screen.
In order to address this shortcoming the annotation is then moved to the next line indented followed by the reason If the reason is not available the annotation stays on the same line as the worktree itself.
The output of "git worktree list
" with verbose becomes like so:
$ git worktree list --verbose
...
/path/to/locked-no-reason acb124 [branch-a] locked
/path/to/locked-with-reason acc125 [branch-b]
locked: worktree with a locked reason
/path/to/prunable-reason ace127 [branch-d]
prunable: gitdir file points to non-existent location
...
git worktree
现在包含在其 man page 中:
For these annotations, a reason might also be available and this can be
seen using the verbose mode. The annotation is then moved to the next line
indented followed by the additional information.
$ git worktree list --verbose
/path/to/linked-worktree abcd1234 [master]
/path/to/locked-worktree-no-reason abcd5678 (detached HEAD) locked
/path/to/locked-worktree-with-reason 1234abcd (brancha)
locked: working tree path is mounted on a portable device
/path/to/prunable-worktree 5678abc1 (detached HEAD)
prunable: gitdir file points to non-existent location
Note that the annotation is moved to the next line if the additional
information is available, otherwise it stays on the same line as the
working tree itself.
并且:
worktree
: teach list
to annotate prunable worktree
Helped-by: Eric Sunshine
Signed-off-by: Rafael Silva
Reviewed-by: Eric Sunshine
The "git worktree list
"(man) command shows the absolute path to the worktree, the commit that is checked out, the name of the branch, and a "locked
" annotation if the worktree is locked, however, it does not indicate whether the worktree is prunable.
The "prune" command will remove a worktree if it is prunable unless --dry-run
option is specified.
This could lead to a worktree being removed without the user realizing before it is too late, in case the user forgets to pass --dry-run
for instance.
If the "list" command shows which worktree is prunable, the user could verify before running "git worktree prune
"(man) and hopefully prevents the working tree to be removed accidentally on the worse case scenario.
Let's teach "git worktree list
" to show when a worktree is a prunable candidate for both default and porcelain format.
In the default format a "prunable" text is appended:
$ git worktree list
/path/to/main aba123 [main]
/path/to/linked 123abc [branch-a]
/path/to/prunable ace127 (detached HEAD) prunable
In the --porcelain
format a prunable label is added followed by its reason:
$ git worktree list --porcelain
...
worktree /path/to/prunable
HEAD abc1234abc1234abc1234abc1234abc1234abc12
detached
prunable gitdir file points to non-existent location
...
git worktree
现在包含在其 man page 中:
branch currently checked out (or "detached HEAD" if none), "locked" if
the worktree is locked, "prunable" if the worktree can be pruned by prune
command.
git worktree
现在包含在其 man page 中:
The command also shows annotations for each working tree, according to its state.
These annotations are:
locked
, if the working tree is locked.
prunable
, if the working tree can be pruned via git worktree prune
.
$ git worktree list
/path/to/linked-worktree abcd1234 [master]
/path/to/locked-worktreee acbd5678 (brancha) locked
/path/to/prunable-worktree 5678abc (detached HEAD) prunable
在 Git 2.36(2022 年第 2 季度)之前,“git worktree list --porcelain
"(man) 没有使用不安全字节正确地引用路径名和锁定原因,这通过引入 NUL 来解决使用“-z”终止输出格式。
见commit d97eb30 (31 Mar 2022) by Phillip Wood (phillipwood
)。
(由 Junio C Hamano -- gitster
-- in commit 7c6d8ee 合并,2022 年 4 月 4 日)
worktree
: add -z option for list subcommand
Signed-off-by: Phillip Wood
Add a -z
option to be used in conjunction with --porcelain
that gives NUL-terminated output.
As 'worktree list --porcelain' does not quote worktree paths this enables it to handle worktree paths that contain newlines.
git worktree
现在包含在其 man page 中:
It is recommended to combine this with -z
.
See below for details.
-z
Terminate each line with a NUL rather than a newline when
--porcelain
is specified with list
.
This makes it possible
to parse the output when a worktree path contains a newline
character.
git worktree
现在包含在其 man page 中:
The porcelain format has a line per attribute.
If -z
is given then the lines
are terminated with NUL rather than a newline.
我需要编写一个 bash 脚本,每小时将我们的存储库分支复制到本地 linux 网络服务器。
我们有一个 git 远程存储库 (gitolite),其分支名为 "master" "testing" "feature-featurename" "hotfix-number"
这些分支中的每一个都应该将其工作树复制到 /var/www/html/branchname
首先:如何将不同的工作树复制到不同的文件夹中? 第二:如果 "feature-" 和 "hotfix-" 分支不断更改名称,我如何自动执行此过程?
这与 git 挂钩无关,这应该是另一个服务器上的脚本 运行,由 cron 作业触发。
可怕的几行:
mkdir -p /var/www/html
git clone --bare user@git-server:/your-repo.git && cd your-repo.git
git for-each-ref --format='%(refname)' refs/heads/ | grep 'branch-pattern' | while read branchRef; do
branchName=${branchRef#refs/heads/}
git archive --format=tar --prefix="$branchName/" "$branchRef" | tar -C/var/www/html -x
done
让我们分解一下:
确保 target 目录存在。可能你不需要。
mkdir -p /var/www/html
克隆git存储库并进入目录
git clone --bare user@git-server:/your-repo.git
列出分支。这将是来自克隆目录的 运行。请注意,未使用
git branch
,因为在脚本中使用时可能会产生令人惊讶的输出。
git for-each-ref --format='%(refname)' refs/heads/
过滤你想要的分支。在您的情况下,模式可能类似于
grep -E "(master)|(testing)|(feature-.*)"
等
grep 'branch-pattern'
while
语句读取每个分支名称并将其分配给branch
变量- 创建一个
branchName
变量,它是不包括 ref 前缀的分支名称。请注意,这是 bash 特定的。 git archive
创建所选分支的 tar 存档,在所有条目前加上分支名称。存档被发送到标准输出
git archive --format=tar --prefix="$branch/" "$branch"
立即将存档提取到其tar获取位置
tar -C/var/www/html -x
所以首先你需要一个分支列表。出于编写脚本的目的,最适合执行此操作的命令是 for-each-ref
。假设您只想要本地分支名称,请使用类似
git for-each-ref refs/heads/* |cut -d\/ -f3
顺便说一句,上面命令中的几件事假设您不在 "namespaces" 中使用分支。如果您使用像 qa/feature-1
这样的分支名称 - 包含 /
- 那么这会改变一些事情。上面的命令就变成了
git for-each-ref refs/heads |cut -d\/ -f3-
但更大的问题是您可能必须更多地考虑分支名称应如何映射到目录名称。所以现在我将继续假设分支名称不包含 /
.
你需要处理每个分支,所以
git for-each-ref refs/heads/* |cut -d\/ -f3 |while read branch; do
# ... will process each branch here
done
现在您可以使用 git worktree
来简化个人结帐。 (请注意,与使用 archive
复制每个分支的整个提交内容,然后调用 tar
撤消您没有执行的工作相比,这应该 多 更有效一开始不想 archive
做。)
确保定义所有必需的工作树
git for-each-ref refs/heads/* |cut -d\/ -f3 |while read branch; do
if [ ! -d .git/worktrees/$branch ]; then
git worktree add /var/www/html/$branch $branch
fi
done
现在关于这一点的一件事是,当分支被移动时(即当收到推送时),它会放置工作树 "out of sync" 这样你似乎已经上演了每个 "undoing"改变推动了。 (默认工作树的保护似乎不适用。)
但这似乎符合您的要求;另一种方法是在推送进来时更新目录,您在问题描述中拒绝了。因此,在这种情况下,您的脚本应该将工作树同步到 "un-undoing" 他们
的新更改git for-each-ref refs/heads/* |cut -d\/ -f3 |while read branch; do
if [ ! -d .git/worktrees/$branch ]; then
git worktree add /var/www/html/$branch $branch
fi
git reset --hard HEAD
done
当然有时分支会消失;如果你不想要陈旧的工作树元数据,你可以添加一个
git worktree prune
您还可以使用git worktree list --porcelain
而不是直接搜索工作树目录——这在奇怪的情况下可能更可取,例如(再次)命名空间分支。
How do I get different worktrees copied into different folders?
这是通过 git 工作树完成的,如 Mark Adelsberger's
答案以:
结尾You also could use
git worktree list --porcelain
instead of searching for worktree directories directly - and that may be preferable in odd cases like (again) namespaced branches.
Git2.36(2022 年第 2 季度,见末尾):
git worktree list --porcelain -z
实际上,您应该,尤其是 Git 2.31(2021 年第一季度):git worktree list
(man) 现在将工作树注释为可修剪,在 [=18= 中显示锁定和可修剪属性]模式,并获得了--verbose
选项。
参见 commit 076b444, commit 9b19a58, commit 862c723, commit 47409e7 (27 Jan 2021), and commit eb36135, commit fc0c7d5, commit a29a8b7 (19 Jan 2021) by Rafael Silva (raffs
)。
(由 Junio C Hamano -- gitster
-- in commit 02fb216 合并,2021 年 2 月 10 日)
worktree
: teachlist
verbose modeHelped-by: Eric Sunshine
Signed-off-by: Rafael Silva
Reviewed-by: Eric Sunshine
"
git worktree list
"(man) annotates each worktree according to its state such as prunable or locked, however it is not immediately obvious why these worktrees are being annotated.
For prunable worktrees a reason is available that is returned byshould_prune_worktree()
and for locked worktrees a reason might be available provided by the user vialock
command.Let's teach "
git worktree list
" a--verbose
mode that outputs the reason why the worktrees are being annotated.
The reason is a text that can take virtually any size and appending the text on the default columned format will make it difficult to extend the command with other annotations and not fit nicely on the screen.
In order to address this shortcoming the annotation is then moved to the next line indented followed by the reason If the reason is not available the annotation stays on the same line as the worktree itself.The output of "
git worktree list
" with verbose becomes like so:$ git worktree list --verbose ... /path/to/locked-no-reason acb124 [branch-a] locked /path/to/locked-with-reason acc125 [branch-b] locked: worktree with a locked reason /path/to/prunable-reason ace127 [branch-d] prunable: gitdir file points to non-existent location ...
git worktree
现在包含在其 man page 中:
For these annotations, a reason might also be available and this can be seen using the verbose mode. The annotation is then moved to the next line indented followed by the additional information.
$ git worktree list --verbose /path/to/linked-worktree abcd1234 [master] /path/to/locked-worktree-no-reason abcd5678 (detached HEAD) locked /path/to/locked-worktree-with-reason 1234abcd (brancha) locked: working tree path is mounted on a portable device /path/to/prunable-worktree 5678abc1 (detached HEAD) prunable: gitdir file points to non-existent location
Note that the annotation is moved to the next line if the additional information is available, otherwise it stays on the same line as the working tree itself.
并且:
worktree
: teachlist
to annotate prunable worktreeHelped-by: Eric Sunshine
Signed-off-by: Rafael Silva
Reviewed-by: Eric Sunshine
The "
git worktree list
"(man) command shows the absolute path to the worktree, the commit that is checked out, the name of the branch, and a "locked
" annotation if the worktree is locked, however, it does not indicate whether the worktree is prunable.The "prune" command will remove a worktree if it is prunable unless
--dry-run
option is specified.
This could lead to a worktree being removed without the user realizing before it is too late, in case the user forgets to pass--dry-run
for instance.
If the "list" command shows which worktree is prunable, the user could verify before running "git worktree prune
"(man) and hopefully prevents the working tree to be removed accidentally on the worse case scenario.Let's teach "
git worktree list
" to show when a worktree is a prunable candidate for both default and porcelain format.In the default format a "prunable" text is appended:
$ git worktree list /path/to/main aba123 [main] /path/to/linked 123abc [branch-a] /path/to/prunable ace127 (detached HEAD) prunable
In the
--porcelain
format a prunable label is added followed by its reason:$ git worktree list --porcelain ... worktree /path/to/prunable HEAD abc1234abc1234abc1234abc1234abc1234abc12 detached prunable gitdir file points to non-existent location ...
git worktree
现在包含在其 man page 中:
branch currently checked out (or "detached HEAD" if none), "locked" if the worktree is locked, "prunable" if the worktree can be pruned by
prune
command.
git worktree
现在包含在其 man page 中:
The command also shows annotations for each working tree, according to its state. These annotations are:
locked
, if the working tree is locked.prunable
, if the working tree can be pruned viagit worktree prune
.$ git worktree list /path/to/linked-worktree abcd1234 [master] /path/to/locked-worktreee acbd5678 (brancha) locked /path/to/prunable-worktree 5678abc (detached HEAD) prunable
在 Git 2.36(2022 年第 2 季度)之前,“git worktree list --porcelain
"(man) 没有使用不安全字节正确地引用路径名和锁定原因,这通过引入 NUL 来解决使用“-z”终止输出格式。
见commit d97eb30 (31 Mar 2022) by Phillip Wood (phillipwood
)。
(由 Junio C Hamano -- gitster
-- in commit 7c6d8ee 合并,2022 年 4 月 4 日)
worktree
: add -z option for list subcommandSigned-off-by: Phillip Wood
Add a
-z
option to be used in conjunction with--porcelain
that gives NUL-terminated output.
As 'worktree list --porcelain' does not quote worktree paths this enables it to handle worktree paths that contain newlines.
git worktree
现在包含在其 man page 中:
It is recommended to combine this with
-z
. See below for details.
-z
Terminate each line with a NUL rather than a newline when
--porcelain
is specified withlist
.This makes it possible to parse the output when a worktree path contains a newline character.
git worktree
现在包含在其 man page 中:
The porcelain format has a line per attribute.
If
-z
is given then the lines are terminated with NUL rather than a newline.