使用普通名称和大写字母名称分别获取Git分支一次

6

我正在获取远程分支,但卡在了某种循环中。

我只获取一次就得到:

$ git fetch
* [new branch]      minorRelease/something-> origin/minorRelease/something

然后我再次获取数据,得到:

$ git fetch
* [new branch]      minorRelease/Something-> origin/minorRelease/Something

同一分支,但首字母大写为S

我尝试从以下文件夹中删除文件.git/refs/remotes/origin/minorRelease,但是当再次获取时,我会得到两个文件并返回上面的循环:

$ git fetch
* [new branch]      minorRelease/Something-> origin/minorRelease/Something
* [new branch]      minorRelease/something-> origin/minorRelease/something

你使用的是哪个平台?它的文件系统是否不区分大小写? - jub0bs
Windows 7,使用 Git Bash。远程服务器运行 Ubuntu,我想。 - ShlomiTC
git remote --verbose show origin | grep "tracked" - jub0bs
2
这不是答案(我没有或使用Windows),但通常git将分支信息存储到文件中。Ubuntu文件系统区分大小写,因此如果有两个分支Abcabc,它们是不同的分支,而Windows(和一些MacOS)文件系统通常是不区分大小写的,所以Abcabc是同一个分支 。这肯定会导致您看到的问题。解决方法是确保没有人在服务器上创建仅在大小写方面有所不同的两个不同的分支。 - torek
另一个选择是切换到支持不区分大小写的文件系统的操作系统。提示:不是Windows :) - jub0bs
@Jubobs - 在 git remote 中,我得到了 minorRelease/Something - ShlomiTC
2个回答

9

@torek 是正确的,这是由于 LinuxWindows 的区别引起的。 Linux 区分大小写,而 Windows 不区分大小写。您可以使用 ls-remote 命令查看服务器上的分支。

git ls-remote --heads origin

在您的情况下,输出应包括仅有 S 不同的两个分支。
ref/heads/minorRelease/Something
ref/heads/minorRelease/something

如果你发现远程分支有重复,可以删除它们。然后再次执行fetch命令即可。
git push origin :minorRelease/Something
git fetch

1
这两个重复了,所以我删除了那个首字母小写的(大写开头是我们的约定)。谢谢。 - ShlomiTC

2
注意:从Git 2.12(2017年第一季度)开始,您可以使用不区分大小写的选项列出分支,这样更容易发现。
请参见提交3bb16a8(2016年12月4日),作者为Nguyễn Thái Ngọc Duy(pclouds
(由Junio C Hamano -- gitster --提交73e494f中合并,2016年12月19日) 、、:增加 --ignore-case 选项以便进行排序和过滤。
此选项可以使排序忽略大小写,当你需要将名为 bug-12-do-something, Bug-12-do-some-moreBUG-12-do-what 的分支分组时非常有用
尽管可以对外部进行排序,但这可能不是一个选择,因为我们会失去来自 git-branch 和 git-tag 的着色和列布局。

过滤也可以这样做,但这可能不太重要,因为如果你急迫的话,总是可以使用丑陋的模式 [bB][uU][gG]-*

然而,你不能同时进行区分大小写的过滤和不区分大小写的排序(或反之亦然)。对于 branchtag 来说,这应该不是问题。作为管道的 for-each-ref 可能需要更精细的控制。
但是,在有需要时,我们总是可以添加 --{filter,sort}-ignore-case

git branch --ignore-case --list

注意:"git for-each-ref"(及其相关命令)的"--ignore-case"选项之前无法正常工作,已在 Git 2.19(2018年第三季度)中修复。
查看提交 639ab5e(2018年7月2日)由Aleksandr Makarov (deviance)完成。
查看提交 e674eb2, 提交 ee0f3e2(2018年7月2日)由Jeff King (peff)完成。
(由Junio C Hamano -- gitster --合并于提交 4301330,2018年7月24日)

ref-filter: 使用 --ignore-case 避免后端过滤

当使用 --ignore-casefor-each-ref 时,我们期望 match_name_as_path() 进行大小写不敏感的匹配。
但是,在我们到达那里之前,还有一个额外的过滤层。自从 commit cfe004aref-filter: limit traversal to prefix, 2017-05-22, Git v2.14.0)以来,我们将前缀提供给 ref 后端,以便它可以优化 ref 迭代。

我们没有机制告诉后端我们正在进行大小写不敏感的匹配。由于打包后端依赖于对引用列表的排序二分搜索,因此在短时间内也不太可能会有这样的机制。
让我们暂时放弃这种情况。额外的过滤是我们无法做到的优化。我们仍然会通过 match_name_as_path() 中的过滤器提供正确的答案。


请注意,自Git 2.23(2019年第三季度)起,“git for-each-ref”支持多个模式的优化已经完成。
请参见提交 b31e268(由Taylor Blau (ttaylorr)于2019年6月26日提交)。
(由Junio C Hamano -- gitster --于2019年7月19日合并至提交b4b8c35

ref-filter.c: find disjoint pattern prefixes

Since cfe004a (ref-filter: limit traversal to prefix, 2017-05-22, Git v2.14.0-rc0), the ref-filter code has sought to limit the traversals to a prefix of the given patterns.

That code stopped short of handling more than one pattern, because it means invoking 'for_each_ref_in' multiple times.
If we're not careful about which patterns overlap, we will output the same refs multiple times.

For instance, consider the set of patterns 'refs/heads/a/*', 'refs/heads/a/b/c', and 'refs/tags/v1.0.0'. If we naïvely ran:

  for_each_ref_in("refs/heads/a/*", ...);
  for_each_ref_in("refs/heads/a/b/c", ...);
  for_each_ref_in("refs/tags/v1.0.0", ...);

we would see 'refs/heads/a/b/c' (and everything underneath it) twice.

Instead, we want to partition the patterns into disjoint sets, where we know that no ref will be matched by any two patterns in different sets.
In the above, these are:

  • {'refs/heads/a/*', 'refs/heads/a/b/c'}, and
  • {'refs/tags/v1.0.0'}

Given one of these disjoint sets, what is a suitable pattern to pass to 'for_each_ref_in'?

One approach is to compute the longest common prefix over all elements in that disjoint set, and let the caller cull out the refs they didn't want.
Computing the longest prefix means that in most cases, we won't match too many things the caller would like to ignore.

The longest common prefixes of the above are:

  • {'refs/heads/a/*', 'refs/heads/a/b/c'} -> refs/heads/a/*
  • {'refs/tags/v1.0.0'} -> refs/tags/v1.0.0

We instead invoke:

  for_each_ref_in("refs/heads/a/*", ...);
  for_each_ref_in("refs/tags/v1.0.0", ...);

Which provides us with the refs we were looking for with a minimal amount of extra cruft, but never a duplicate of the ref we asked for.

Implemented here is an algorithm which accomplishes the above, which works as follows:

  1. Lexicographically sort the given list of patterns.

  2. Initialize 'prefix' to the empty string, where our goal is to build each element in the above set of longest common prefixes.

  3. Consider each pattern in the given set, and emit 'prefix' if it reaches the end of a pattern, or touches a wildcard character. The end of a string is treated as if it precedes a wildcard. (Note that there is some room for future work to detect that, e.g., 'a?b' and 'abc' are disjoint).

  4. Otherwise, recurse on step (3) with the slice of the list corresponding to our current prefix (i.e., the subset of patterns that have our prefix as a literal string prefix.)

This algorithm is 'O(kn + n log(n))', where 'k' is max(len(pattern)) for each pattern in the list, and 'n' is len(patterns).

By discovering this set of interesting patterns, we reduce the runtime of multi-pattern 'git for-each-ref' (and other ref traversals) from O(N) to O(n log(N)), where 'N' is the total number of packed references.

Running 'git for-each-ref refs/tags/a refs/tags/b' on a repository with 10,000,000 refs in 'refs/tags/huge-N', my best-of-five times drop from:

  real    0m5.805s
  user    0m5.188s
  sys     0m0.468s

to:

  real    0m0.001s
  user    0m0.000s
  sys     0m0.000s

On linux.git, the times to dig out two of the latest -rc tags drops from 0.002s to 0.001s, so the change on repositories with fewer tags is much less noticeable.


"git branch"和其他 "for-each-ref" 变体接受多个 --sort=<key> 选项,按优先级递增排序,但在处理 "--ignore-case" 和参考名称的决定性因素时出现了一些故障,在 Git 2.27(2020年第二季度)中已得到修复。
查看 提交 7c5045f提交 76f9e56(2020年5月3日)由Jeff King (peff)
(由Junio C Hamano -- gitster --提交6de1630中合并,2020年5月8日)

ref-filter: apply --ignore-case to all sorting keys

Signed-off-by: Jeff King

All of the ref-filter users (for-each-ref, branch, and tag) take an --ignore-case option which makes filtering and sorting case-insensitive.

However, this option was applied only to the first element of the ref_sorting list.

So:

git for-each-ref --ignore-case --sort=refname

would do what you expect, but:

git for-each-ref --ignore-case --sort=refname --sort=taggername

would sort the primary key (taggername) case-insensitively, but sort the refname case-sensitively. We have two options here:

  • teach callers to set ignore_case on the whole list

  • replace the ref_sorting list with a struct that contains both the list of sorting keys, as well as options that apply to all keys

I went with the first one here, as it gives more flexibility if we later want to let the users set the flag per-key (presumably through some special syntax when defining the key; for now it's all or nothing through --ignore-case).

The new test covers this by sorting on both tagger and subject case-insensitively, which should compare "a" and "A" identically, but still sort them before "b" and "B".
We'll break ties by sorting on the refname to give ourselves a stable output (this is actually supposed to be done automatically, but there's another bug which will be fixed in the next commit).


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接