为什么 git reset 不能拉取请求 [英] Why cannot git reset to pull request

查看:80
本文介绍了为什么 git reset 不能拉取请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有时我登录云实例,拉取一个仓库,然后想尝试拉取请求.我现在使用的命令是

git fetch origin pull//head &&git 结帐 FETCH_HEAD

很长.我也试过更短的方法

git reset --hard origin/pull/git reset --hard origin/pull//headgit reset --hard origin/pull//HEAD

出现以下错误

$ git reset --hard origin/pull/27致命:模棱两可的论点起源/拉/27":未知修订或路径不在工作树中.使用--"将路径与修订分开,如下所示:'git <命令>[<修订版>...] -- [<文件>...]'

为什么 git reset --hard origin/<some-branch> 可以工作,但拉取请求分支不行?

我注意到在

的输出中

$ git ls-remote origin

常规分支和拉取请求分支之间存在差异.例如

<块引用>

c31a55 refs/heads/fix-async-stdout-order

615f5a refs/pull/10/head

headspull 有何不同?(我在此处缩短了哈希值,以便在视觉上更清晰)

解决方案

每个 Git 存储库都有自己的名称副本.每个名称都映射到一个哈希 ID,例如,在您的示例中:

<块引用><块引用>

c31a55 refs/heads/fix-async-stdout-order615f5a 参考/拉/10/头

您建议将 refs/heads/fix-async-stdout-order 映射到哈希 ID c31a55refs/pull/10/head 映射到哈希 ID 615f5a.1

名称 是 Git 称为 refsreferences 的东西.(哈希 ID 是您现在应该非常熟悉的哈希.)

在大多数情况下,当您为 Git 命名时,Git 会立即将其转换为底层哈希 ID.散列 ID 才是真正重要的:提供名称主要是为了让我们这些普通人能够处理作为散列 ID 的真实名称.哈希 ID 永远不会改变:它们总是唯一标识特定内容——例如,一个特定的提交.提交及其哈希 ID 是永久的,而一个或多个名称可以随意创建或销毁.2

当一个名称标识一个提交时,我们可以直接使用名称来查找该提交:refs/heads/fix-async-stdout-order,例如标识提交c31a55.但是提交也允许我们找到它们的直接提交:从 c31a55 我们可以向后工作到它的父是什么,并且从那个父我们可以向后工作另一步到另一个提交,依此类推,一直回到存储库中时间的开始.因此,像这样的名称不仅用于查找其存储哈希 ID 的提交,还用于查找链中所有较早的提交.

当名称是 branch 名称时——正如这个名称一样——Git 还允许我们专门与 git checkout 一起使用它.git checkout 命令将另一个特殊名称 HEAD 附加到分支名称.Git 现在认为我们在"分支上,所以如果我们提交,Git 将自动更改存储在该分支名称下的哈希 ID.

即之后:

git checkout fix-async-stdout-order

如果我们做一些工作然后进行提交,名称fix-async-stdout-order——实际上是refs/heads/fix-async-stdout-order,我们只是为了显示目的而缩短了它——将停止指向提交 c31a55 并开始指向我们的新提交.我们的新提交将 c31a55 作为其父级.

这个能够在"分支上的属性只允许用于分支名称.Git 有很多名称:分支名称、标签名称、远程跟踪名称等等.它们都是引用,但只有拼写以refs/heads/开头的引用才是分支名称.

引用refs/tags/v1.2,如果存在,就是名为v1.2tag.

引用refs/remotes/origin/master,如果存在,就是远程跟踪名称 origin/master.>

这些前缀中的每一个——refs/heads/refs/tags/refs/remotes/——都代表一个 命名空间.refs/heads/ 命名空间中的引用是分支名称.

除了在允许 git checkout 以上述方式使用它们方面的特殊性之外,分支 名称的另一个特殊功能发生在客户端 Git 时——例如作为你自己的 Git——连接到服务器 Git.服务器 Git 为客户端显示其所有名称,包括分支名称,以及这些名称所代表的哈希 ID.客户端 Git 复制服务器的分支名称但同时更改,这样服务器调用refs/heads/master,客户端调用refs/remotes/origin/master.

这个过程是您的 Git 首先如何拥有远程跟踪名称的.服务器 Git 有它的分支,当你运行 git fetch 时,你的 Git 出现了——并且看到然后记住它们的分支作为你的远程跟踪origin/* 名称.这些位于您的 Git 中,位于 refs/remotes/ 命名空间中.

这个过程发生在分支名称上!3因为 refs/pull/10/head 不以 refs/开头head/,它不是分支名称.该过程不适用于 refs/pull/10/head.所以这就是 headspull 不同的原因和方式.

<小时>

1这些都是缩写哈希ID;实际的哈希 ID 目前总是 40 个字符长.

2这里的警告是,如果没有允许您查找提交或其他 Git 对象的名称,该提交或其他对象现在不受保护 来自 Git 的垃圾收集过程.因此,名称不仅可以让我们找到链中最后的提交,还可以保护该提交它的所有前辈不被视为垃圾.

3该过程是可编程的,通过 Git 调用的refspecs.上面的描述仅适用于您在运行 git clone 时获得的 default refspecs.如果您编写自己的 refspecs,则可以更改此处发生的情况.请参阅git fetch 文档 并注意remote.origin.fetch 是一个累积设置;remote.origin.fetch 的每个实例提供一个引用规范.

克隆时的默认设置是 Git 创建一个 remote.origin.fetch 设置,该设置要么将其分支的所有复制到您的远程跟踪名称,要么如果您在克隆期间选择了 --single-branch,则将 一个 的分支复制到 一个 远程跟踪名称.

Sometimes I log onto a cloud instance, pull a repo, and then want to try a pull request. The command I use now is

git fetch origin pull/<ID>/head && git checkout FETCH_HEAD

which is long. I also tried shorter ways

git reset --hard origin/pull/<ID>
git reset --hard origin/pull/<ID>/head
git reset --hard origin/pull/<ID>/HEAD

which give the following error

$ git reset --hard origin/pull/27
 fatal: ambiguous argument 'origin/pull/27': unknown revision
 or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

Why does git reset --hard origin/<some-branch> work but not the pull request branch?

I notice that in the output of

$ git ls-remote origin

there is a difference between a regular branch and a pull request branch. For example

c31a55 refs/heads/fix-async-stdout-order

615f5a refs/pull/10/head

How does heads differ from pull? (I have shorten the hash here so it's visually cleaner)

解决方案

Every Git repository has its own copies of names. Each name maps to exactly one hash ID, e.g., in your sample:

c31a55 refs/heads/fix-async-stdout-order
615f5a refs/pull/10/head

you've suggested that refs/heads/fix-async-stdout-order maps to hash ID c31a55, and refs/pull/10/head maps to hash ID 615f5a.1

The names are things that Git calls refs or references. (The hash IDs are the hashes that you should be quite familiar with by now.)

In most cases, when you give Git a name, Git just immediately turns it into the underlying hash ID. The hash IDs are what really matter: the names are mainly provided to allow us, mere humans, to deal with the real names that are the hash IDs. The hash IDs never change: they always uniquely identify that particular content—that one specific commit, for instance. The commit and its hash ID are forever, while one or more names can be created or destroyed at will or whim.2

When a name identifies a commit, we can use the name directly to find the commit: refs/heads/fix-async-stdout-order, for instance, identifies commit c31a55. But commits also allow us to find their immediate parent commits: from c31a55 we can work backwards to whatever its parent is, and from that parent we can work backwards another step to another commit, and so on, all the way back to the beginning of time in the repository. So a name like this not only serves to find the one commit whose hash ID it stores, but also all earlier commits in its chain.

When the name is a branch name—as this one is—Git also allows us to use it specially with git checkout. The git checkout command attaches another special name, HEAD, to the branch name. Git now considers us to be "on" the branch, so that if we make new commits, Git will automatically change the hash ID stored under that branch name.

That is, after:

git checkout fix-async-stdout-order

if we do some work and then make a new commit, the name fix-async-stdout-order—which is really refs/heads/fix-async-stdout-order, we've just shortened it for display purposes—will stop pointing to commit c31a55 and start, instead, to point to our new commit. Our new commit will have c31a55 as its parent.

This property, of being able to get "on" a branch, is only allowed for branch names. Git has many names: branch names, tag names, remote-tracking names, and so on. All of them are references, but only references whose spelling starts with refs/heads/ are branch names.

The reference refs/tags/v1.2, if it exists, is the tag named v1.2.

The reference refs/remotes/origin/master, if it exists, is the remote-tracking name origin/master.

Each of these prefixes—refs/heads/, refs/tags/, and refs/remotes/—represents a namespace. References in the refs/heads/ namespace are branch names.

Besides being special in terms of allowing git checkout to use them in the way described above, the other special feature of a branch name occurs when a client Git—such as your own Git—connects to a server Git. The server Git displays, for the client, all of its names, including the branch names, along with the hash IDs that those names represent. The client Git copies down the server's branch names but changes them at the same time, so that what the server calls refs/heads/master, the client calls refs/remotes/origin/master.

This process is how your Git comes to have remote-tracking names in the first place. The server Git has its branches, and your Git comes along—when you run git fetch—and sees and then remembers their branches as your remote-tracking origin/* names. These live in your Git, in the refs/remotes/ namespace.

This process occurs only for branch names!3 Since refs/pull/10/head does not start with refs/heads/, it is not a branch name. The process does not apply to refs/pull/10/head. So that's why and how heads differs from pull.


1These are both abbreviated hash IDs; actual hash IDs are currently always 40 characters long.

2The caveat here is that without a name that allows you to find a commit, or other Git object, that commit or other object is now unprotected from Git's garbage collection process. So not only do names allow us to find the last commit in a chain, they also protect that commit and all of its predecessors from being taken out as trash.

3The process is programmable, through what Git calls refspecs. The description above applies only to the default refspecs that you get when you run git clone. If you make up your own refspecs, you can change what happens here. See the git fetch documentation and note that remote.origin.fetch is a cumulative setting; each instance of remote.origin.fetch provides one refspec.

The default at clone time is for Git to create one remote.origin.fetch setting, that either copies all of their branches to your remote-tracking names, or copies one of their branches to one remote-tracking name if you opted for --single-branch during cloning.

这篇关于为什么 git reset 不能拉取请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆