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

让我们分解一下:

  1. 确保 target 目录存在。可能你不需要。
    mkdir -p /var/www/html

  2. 克隆git存储库并进入目录
    git clone --bare user@git-server:/your-repo.git

  3. 列出分支。这将是来自克隆目录的 运行。请注意,未使用 git branch,因为在脚本中使用时可能会产生令人惊讶的输出。
    git for-each-ref --format='%(refname)' refs/heads/

  4. 过滤你想要的分支。在您的情况下,模式可能类似于 grep -E "(master)|(testing)|(feature-.*)"
    grep 'branch-pattern'

  5. while语句读取每个分支名称并将其分配给branch变量

  6. 创建一个 branchName 变量,它是不包括 ref 前缀的分支名称。请注意,这是 bash 特定的。
  7. git archive 创建所选分支的 tar 存档,在所有条目前加上分支名称。存档被发送到标准输出
    git archive --format=tar --prefix="$branch/" "$branch"

  8. 立即将存档提取到其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.