Git分支未显示所有分支 [英] Git branch is not displaying all branches

查看:209
本文介绍了Git分支未显示所有分支的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是使用Git的新手,我从GitHub克隆了一个分支,当我键入 git branch 时,所有分支都会显示出来.完成工作后,我将其成功推送到新分支.之后,我将该文件夹复制到另一个目录(因为我想进行备份以避免冲突),输入该文件,然后键入 git branch .仅显示3个分支,就知道我在Github上有4个分支.

我试图通过将分支克隆到新文件夹(键入 git clone -b< branch-name>< repo-link-https> )内来解决该问题,现在仅将分支我克隆的东西出现了.

有什么建议吗?

解决方案

克隆现有存储库时,您的Git会创建一个新的不同存储库,并将其复制到此新存储库中 all 1 原始存储库中的提交和分支中的 . git clone last 步骤是创建一个分支.该分支名称是您的您的,而不是他们的;只是与他们的名字之一拼写相同.

在使用克隆(一个不同的存储库)时,您可以向它添加越来越多的分支.如果将原始存储库中的所有相同分支添加到其中,那么您现在将拥有其所有提交的所有分支名称(作为自己的分支,请记住你).但是直到那时,您才拥有他们所有的 commits .很好,因为Git与分支无关.Git与 commits 有关.


1 确切的描述要比这复杂得多,但是可以将其视为复制所有提交,不复制任何分支".会帮助您入门.


我试图通过将分支克隆到新文件夹(键入 git clone -b )中来解决此问题,现在仅出现我克隆的分支.

创建新克隆(又是一个 new 信息库)时,您将获得前一个信息库的所有提交,但没有分支的所有信息-最后一个 git clone 命令的步骤是运行一个分支的 git checkout git switch 命令 2 . -b 标志存在,因此您可以在最后一步中告诉Git 要复制其分支名称的.如果您省略 -b 标志,您的Git会询问他们的Git存储库-您正在克隆的Git存储库-推荐哪个分支 .但是无论哪种方式,您只会得到一个分支.

您实际上不需要任何分支名称即可在Git中工作.不过,您确实需要 some 这样的名称,而分支名称是此处最好的名称.这就是为什么您的Git在 git clone 过程结束时使用一个名字的原因.您输入的每个名称都会给您带来更多好处.

要了解发生了什么,请继续阅读.如果您对已解决您的紧迫问题感到满意,则可以在此处停止.


2 Git版本2.23中首先添加了 git switch 命令,以将过于复杂的 git checkout 命令分为两个单独的部分命令, git switch git restore .现有的 git checkout 保留;您可以使用它代替两个新的,更简单的命令.不过,从某种意义上讲,新的简化命令更安全: git switch 命令试图非常安全,复制的 git checkout 的一半也是如此.但是, git restore 命令是故意不安全的,因为它将不可撤消地破坏工作.它会复制 git checkout 的另一半.因此,如果您使用 git checkout ,则可能会意外地调用销毁我的作品"当您思考一半时,您正在调用安全地做事";一半.


Git就是关于提交的

要了解Git在这里做什么以及为什么这样做,请从以下事实开始:Git本身就是关于提交的.它与分支无关,尽管分支名称可以帮助您(和Git)查找提交.它与文件无关,尽管会提交 contain 文件.这实际上与提交有关:Git所做的其他所有工作都是为了保留和添加提交.提交是一切开始的地方,也是其他一切目的.

这意味着了解什么是提交 ,如何命名特定提交以及如何进行 new 提交至关重要.让我们从名称开始.

提交的真实名称是其哈希ID

您可能会认为分支名称会命名一个提交,并且确实是间接的.实际上,每个提交都通过编号来命名.每个提交都有一个唯一的编号.没有其他提交可以拥有该编号:提交该提交后,该编号将分配给该那个提交.因为该提交将永远占用该数字,所以该数字必须非常大,而且确实如此.当前,每个Git提交都从2 160 个可能的数字中获得一个. 3 这个数字以十六进制表示为一个大的丑陋字符串,例如 this 提交比上一个提交更好?或至少,如果不一定更好,为什么会有所不同.此特定提交的目标可能是修复一些错误,或添加一些新功能,或准备就绪以添加新功能,等等.提交本身具有更新的源代码,但不一定与更新应解决的 bug 有关.这是您解释的机会.

Git会为您生成一部分元数据,然后再使用它,您很少会直接看到它,那就是:每个提交都保留其直接的前一个提交的原始哈希ID.这些字符串一起向后提交向后,组成一连串的提交,最后一次提交结束.

我们可以画这个.想象一下,我们有一个只有三个提交的存储库.代替真实的哈希ID,我们将使用单个大写字母来代表提交.第一个提交是 A ,下一个提交是 B ,第三个提交是提交 C :

  A< -B< -C 

由于提交 C 是最后一个,因此它在元数据中具有更早提交 B 的哈希ID.我们说 C 指向 B .同样,提交 B 指向 A .由于 A 是有史以来的第一次提交,因此它缺少此向后箭头:它不会指向任何地方.Git将此称为(或) root commit .这是我们停止向后工作的地方.

我刚才提到,每个提交都有每个文件的完整快照.但是,如果您对Git show 进行了提交,那么Git会向您显示更改的内容.Git如何以及为什么这样做?

为什么为什么最容易解释.如果要查看提交中所有 的文件,只需签出该提交即可.Git将从提交中复制所有这些文件(记住,这些文件以特殊的冻结Git格式存储,并已重复数据删除(也压缩))到普通的普通计算机文件中.您可能有一堆文件查看器,其功能比Git强大得多:它们可以向您显示图像,在文本编辑器中打开文本文档,在PDF查看器中打开PDF,等等.但是您的文件查看器可能不能将整个快照与以前的整个快照进行比较.Git 可以.

Git可以很容易地将快照 C 与快照 B 进行比较,因为提交 C 拥有提交 B 的哈希ID.因此,Git可以仅提取提交.而且,由于Git可以对文件进行重复数据删除,因此Git可以立即知道(甚至是 bother )提取重复的文件.Git只需要提取并比较不同文件.Git将做到这一点,并将构建一组更改,将旧文件转换为新文件.这就是Git向您显示的内容:这套说明.

(请注意,Git按需创建指令集.在您要求Git比较任意两个提交之前,所有Git都是两个快照.您可以根据选项获得不同的指令集传递给比较命令,例如,Git可以基于单词进行差异检查,或者忽略某些类型的空白更改.Git的功能并不总是像我们想要的那样好,但是有一些技巧我们可以使用.但超出了此特定答案的范围.)

通过分支名称查找提交

我们已经知道,如果我们记住大的丑陋的哈希ID(或将其写下来),则可以使用它们来查找提交.但这太荒谬了.我们有一台计算机.为什么我们不让计算机为我们写下哈希ID?

这是分支名称的作用.但这有点偷偷摸摸.分支名称的真正作用是仅存储 last 提交的哈希ID.让我们再次绘制该三提交存储库,并添加一个名称 main ,该名称标识 last 提交:

  A--B--C<-主要 

在这里,我们只是想知道 main 这个名字对我们有用,而不是试图记住 C 的哈希ID.因此, git checkout main (2.23之前的Git版)或 git switch main (2.23及更高版本)为我们提供了最新的提交-当前为 C 不管它具有什么哈希ID.

我们现在可以添加一个新名称,该名称也指向提交 C :

  A--B--C<-main,dev 

现在,我们还需要做一件事:我们使用以下哪个名称?现在,这无关紧要,因为两个名称都选择commit C .但是,让我们将特殊名称 HEAD 附加到两个分支名称之一中,如下所示:

  A--B--C<-main(HEAD),开发 

如果我们 git switch dev ,我们将特殊名称 HEAD 重新附加到名称 dev ,如下所示:

  A--B--C<-main,dev(HEAD) 

现在让我们进行一次 new 提交.不用担心如何进行新提交,我们仅假设已完成.这个新的提交 D 必然会指向现有的提交 C ,因为我们是从> C .看起来像这样:

  A--B--C\d 

但是 D 现在是最新提交,因此Git必须更新名称.应该更新哪个名字?答案很明确:应该更新 HEAD 所附加的内容:

  A--B--C<-主要\D<-dev(头) 

我们现在有两个分支名称,两个名称指定两个不同最新"名称,提交. main 上的最新提交是 C ,而 dev 上的最新提交是 D .提交 D 指向提交 C ,指向 B ,指向 A ;因此,所有四个提交都在 分支 dev 上,而其中三个提交在 main 上.

如果我们切换回名称 main 并在那里进行新的提交,则会得到:

  E<-main(HEAD)/A--B--C\开发者 

这意味着我们现在在两个分支上共享三个提交,一个提交仅在 main 上,一个提交仅在 dev 上.现在,我们需要 两个名称来查找所有五个提交;一个名称将找到一个提交,而该名称将找到三个共享提交,但是我们需要另一个名称来找到最后剩余的提交.

请注意,分支名称为 move .实际上,当我们进行新的提交时,它们会自动移动:附加了 HEAD 的分支名称都会自动移动以包含新的提交.所有其他分支名称都保留在该位置,但是由于它们是我们的分支名称,因此我们可以控制.我们可以随时让Git移动这些名称.唯一的限制是我们必须具有 commit 才能将名称移至.

克隆将创建远程跟踪名称

当我们克隆别人的存储库时,我们会得到他们的所有提交,而没有他们的任何分支.这是如何运作的?好吧,假设我们具有上述内容,并使用两个实际的分支名称 main dev 分别选择提交 E D .现在,我们创建一个 new 存储库,在其中复制所有五个提交,从而得到:

  E/A--B--C\d 

实际上,我们确实需要两个名称来查找所有提交.但是我们不需要分支名称.另一个与其他存储库一起使用的Git具有分支名称,因为那些是 his 分支,他在进行新提交时会四处移动.因此,我们的Git要做的是复制其名称,但更改它们.我们的Git会使用其分支名称,并通过在名称中添加通常是 origin/的内容来创建远程跟踪名称.sup> 5 这样我们得到:

  E<-来源/主要/A--B--C\D<-origin/dev 

Git将拒绝将特殊名称 HEAD 附加到这些远程跟踪名称之一.仅允许 HEAD 附加到分支名称.因此,我们 git clone 的最后一步是使用 -b 选项或他们的建议来选择这两个名称之一,并从中创建一个分支名称,像这样:

  E<-main(HEAD),来源/main/A--B--C\D<-origin/dev 

请注意,我们的分支名称选择相同的提交作为我们的 git克隆分支名称.但是我们现在只有一个分支名称,而没有两个.如果我们运行:

  git switch开发 

这使用了Git提供的特殊功能,该功能可以找到其 origin/dev 并创建我们自己的新名称 dev :

  E<-主,原点/主/A--B--C\D<-dev(HEAD),原始/dev 

现在,我们有两个分支名称.但是我们最初不是.请注意,我们现在还签出了提交 D ,而不是提交了 E ,因为 git switch (或 git checkout ,如果使用的话)不仅会切换分支,还会选择分支名称标识的提交作为要检出的提交,因此可供我们使用.


5 从技术上讲,远程跟踪名称位于单独的 命名空间 .我们的Git不仅在前面添加了 origin/,还将 refs/heads/替换为 refs/remotes/origin/.名称 origin 实际上是一个 remote ,我们可以在Git存储库中拥有任意数量的遥控器.但这是另一个问题的话题.

I'm new to using Git, I cloned a branch from GitHub and all branches are displayed when I typed git branch. After finishing my work, I pushed it to a new branch successfully. After that, I copied the folder to another directory (because I wanted to have a backup to avoid conflicts), entered it, and typed git branch. Only 3 branches displayed, knowing that I have 4 on Github.

I tried to solve the issue by cloning the branch inside a new folder (typed git clone -b <branch-name> <repo-link-https>) and now only the branch that I cloned is appearing..

Any suggestions please?

解决方案

When you clone an existing repository, your Git makes a new and different repository, and copies into this new repository all1 of the commits and none of the branches from the original repository. The last step of git clone is to create one branch. This branch name is yours, not theirs; it's just spelled the same as one of their names.

As you work with your clone—a different repository—you can add more and more branches to it. If you add to it all the same branches that are in the original repository, you now have all of their commits and all of their branch names (as your own branches, mind you). But until then, you just have all of their commits. That's fine, because Git isn't about branches. Git is about commits.


1The precise description is much more complicated than this, but thinking of it as "copy all of their commits and none of their branches" will get you started.


I tried to solve the issue by cloning the branch inside a new folder (typed git clone -b) and now only the branch that I cloned is appearing..

When you make a new clone—which, again, is a new repository, where you get all of the previous repository's commits but none of its branches yet—the last step of the git clone command is to run a git checkout or git switch command2 that makes one branch. The -b flag exists so that you can tell your Git which of their branch names to copy, as the last step. If you omit the -b flag, your Git asks their Git repository—the one you're cloning—which branch they recommend. But either way you get only one branch.

You don't actually need any branch names to do work in Git. You do need some kind of name, though, and branch names are the best kind of name here. That's why your Git makes one name at the end of the git clone process. Each name you make gives you one more thing to work with.

To understand what's going on, read on. If you're satisfied that your immediate question has been answered, you can stop here.


2The git switch command was first added in Git version 2.23, to split up the overly-complicated git checkout command into two separate commands, git switch and git restore. The existing git checkout remains; you can use it instead of the two new, simpler commands. The new simplified commands are in a sense safer, though: the git switch command tries to be very safe, as does the half of git checkout that it copied. The git restore command, however, is deliberately unsafe in that it will irrevocably destroy work; it copies the other half of git checkout. So if you use git checkout, you can accidentally invoke the "destroy my work" half when you think you're invoking the "safely do stuff" half.


Git is all about commits

To understand what Git is doing here and why it does this like this, start with the fact that Git itself is really all about commits. It's not about branches, though branch names help you (and Git) find commits. It's not about files, though commits contain files. It's really about the commits: everything else Git does is in service of retaining and adding commits. The commits are where things start, and are the purpose of everything else.

This means it's crucial to understand what a commit is, how you name a particular commit, and how you make a new commit. Let's start with the name.

The true name of a commit is its hash ID

You might think that a branch name would name a commit—and it sort of does, but indirectly. In fact, every commit is named by its number. Each commit has a unique number. No other commit can ever have that number: once that commit is made, that number is allocated to that commit. Because that commit takes up that number forever, the number has to be really big, and it is. Currently, each Git commit gets one out of 2160 possible numbers.3 This number gets expressed in hexadecimal as a big ugly string like e31aba42fb12bdeb0f850829e008e1e3f43af500 (this is an actual commit in a Git repository for Git itself).

This number always works: if you have this commit, that is its number, and git show e31aba42fb12bdeb0f850829e008e1e3f43af500 will show it, for instance. You can usually abbreviate the number, to as little as the first four characters if that's unambiguous, so if you have a clone of the Git repository for Git, git show e31aba42fb12bdeb0f850829e008 is almost guaranteed to work. But git show e31a doesn't because it could be short for this commit, or for commit e31a17f741..., for instance. While e31ab works today, as more commits get added, it might stop working.

These numbers look random, but aren't. In fact, each one is a cryptographic checksum of the complete contents of the commit.4 Git does a double-check when extracting any of its internal objects, including commits, that the checksum still matches, so as to detect storage failures: you tell Git to find a commit (or other object) by hash ID and it checks that the hash ID still matches. So this in turn means that no part of any commit—or any of Git's other internal objects—can ever change, either. You can make new ones, each of which gets a new and different ID, but this does not affect the existing ones, which remain in the repository.


3There are plans to redo the numbering system to use 2256 numbers, with some kind of ugly transition.

4In fact, all of Git's internal objects use this scheme. This means all saved objects are frozen for all time. This is how Git freezes and de-duplicates file contents, for instance.


What's in a commit

Now that we know one—and the deepest, as it were—way to look up a commit, by its hash ID, it's time to look at what's inside each commit. Each commit has two parts:

  • A commit holds a full snapshot of all your files. This is the main data of most commits (and usually also the bulk of the repository). Each file is stored as an internal blob object, using this same hash-name-encoding trick. This automatically de-duplicates files, so that if you make a hundred commits in a row that mostly re-use most of their files, they don't really take any extra space.

  • Each commit also holds some metadata, or information about the commit itself: who made it, when, and why, for instance. The "why" part is your log message: your own explanation to yourself and/or others later. Why is this commit better than the last one? Or at least, why it is any different, if it's not necessarily better. The goal of this particular commit might be to fix some bug, or add some new feature, or make something ready to add a new feature, or whatever. The commit itself has the updated source code, but not necessarily anything about the bug that the update is supposed to fix. This is your chance to explain that.

There's a piece of the metadata that Git generates for you, and then uses later, that you rarely see directly, and that is this: Each commit holds the raw hash ID of its immediate predecessor commit. This strings commits together, backwards, into a chain of commits that ends with the latest commit.

We can draw this. Imagine we have a repository with just three commits in it. Instead of real hash IDs, we'll use single uppercase letters to stand in for the commits. The very first commit will be A, the next will be B, and the third commit is commit C:

A <-B <-C

Since commit C is the last one, it has earlier commit B's hash ID in its metadata. We say that C points to B. By the same token, commit B points to A. Since A is the very first commit ever made, it lacks this backwards-pointing arrow: it does not point anywhere. Git calls this a (or the) root commit. It's where we get to stop working backwards.

I mentioned just a moment ago that each commit has a full snapshot of every file. But if you have Git show a commit, Git shows you what changed. How and why does Git do this?

The why is perhaps the easiest to explain. If you want to see all the files that are in the commit, you can just check out the commit. Git will copy all those files out of the commit—where, remember, they're stored in a special frozen Git format, de-duplicated (and compressed too)—to regular ordinary computer files. You probably have a bunch of file viewers that are more competent than Git could ever be: they can show you images as images, open textual documents in text editors, open PDFs with PDF viewers, and so on. But your file-viewer probably can't compare the entire snapshot with the previous entire snapshot. Git can.

Git can compare snapshot C against snapshot B pretty easily, because commit C holds commit B's hash ID. So Git can just extract both commits. Moreover, because of the way Git de-duplicates files, Git can immediately know—and not even bother extracting—the duplicated files. Git needs only to extract and compare the different files. Git will do that, and will construct a set of changes that will turn the old files into the new ones. That's what Git will show you: this set of instructions.

(Note that Git creates the set of instructions on demand. Until you ask Git to compare any two commits, all Git has are the two snapshots. You can get different sets of instructions based on options you pass to the comparison command. For instance, Git can do the difference-checking based on words, or otherwise ignore certain kinds of white-space changes. Git's abilities here are not always as good as we might like, but there are some tricks we can use. They're out of scope for this particular answer, though.)

Finding commits by branch names

We already know that if we memorize the big ugly hash IDs (or write them down), we can use those to find commits. But this is ridiculous. We have a computer. Why don't we have the computer write down the hash IDs for us?

This is what a branch name does. But it's a little sneaky. What a branch name really does is store just the last commit's hash ID. Let's draw that three-commit repository again, and add a name, main, that identifies the last commit:

A--B--C   <-- main

Here, instead of trying to remember C's hash ID, we just know that the name main does that for us. So git checkout main (pre-2.23 Git) or git switch main (2.23 and later) gets us the latest commit—currently C—no matter what hash ID it has.

We can now add a new name that also points to commit C:

A--B--C   <-- main, dev

Now we need one more thing: which of these names are we using? Right now, it doesn't matter much, because both names select commit C. But let's attach the special name HEAD to one of the two branch names, like this:

A--B--C   <-- main (HEAD), dev

If we git switch dev, we're re-attaching the special name HEAD to the name dev, like this:

A--B--C   <-- main, dev (HEAD)

Now let's make a new commit. Without worrying about how we make a new commit, let's just assume that it's all done. This new commit D will, necessarily, point back to existing commit C, because we made D from C. So that looks like this:

A--B--C
       \
        D

But D is now the latest commit, so Git has to update a name. Which name should it update? The answer is clear: it should update the one that HEAD is attached-to:

A--B--C   <-- main
       \
        D   <-- dev (HEAD)

We now have two branch names, and the two names specify two different "latest" commits. The latest commit on main is C, and the latest commit on dev is D. Commit D points back to commit C, which points back to B, which points back to A; so all four commits are on branch dev, while three of them are on main.

If we switch back to the name main and make new a new commit there, we get:

        E   <-- main (HEAD)
       /
A--B--C
       \
        D   <-- dev

which means we now have three commits that are shared on both branches, and one commit that is only on main and one commit that is only on dev. Now we need both names to find all five commits; one name will find one commit, which will find the three shared commits, but we need the other name to find the last remaining commit.

Note that the branch names move. In fact, they move automatically, as we make new commits: whichever branch name has HEAD attached to it automatically moves to encompass the new commit. All the other branch names stay in place at that point, but because they are our branch names, we are in control. We can have our Git move those names any time we like. The only constraint is that we have to have a commit to move the name to.

Cloning creates remote-tracking names

When we clone someone else's repository, we get all their commits and none of their branches. How does this work? Well, suppose we have the above, with two actual branch names main and dev selecting commits E and D respectively. We now make a new repository where we copy all five commits, giving us:

        E
       /
A--B--C
       \
        D

We do actually need two names to find all the commits. But we don't need branch names. The other Git, working with the other repository, has branch names, because those are his branches that he'll move around as he makes new commits. So what our Git does is copy their names but change them. We have our Git take their branch names and create our remote-tracking names, by adding something—usually origin/—to the names.5 So we get:

        E   <-- origin/main
       /
A--B--C
       \
        D   <-- origin/dev

Git will refuse to attach the special name HEAD to one of these remote-tracking names. HEAD is only allowed to attach to a branch name. So the last step of our git clone is to use the -b option, or their recommendation, to pick one of these two names, and create a branch name from it, like this:

        E   <-- main (HEAD), origin/main
       /
A--B--C
       \
        D   <-- origin/dev

Note that our branch name selects the same commit as the remote-tracking name that our git clone made from their branch name. But we now have only one branch name, not two. If we run:

git switch dev

this uses a special feature that Git provides, that finds their origin/dev and creates our own new name dev:

        E   <-- main, origin/main
       /
A--B--C
       \
        D   <-- dev (HEAD), origin/dev

and now we have two branch names. But we didn't initially. Note that we also now have commit D checked out, rather than commit E, because git switch (or git checkout, if we use that) not only switches branches, but also selects the commit that the branch name identifies, as the commit that is to be checked-out, and therefore available to us to work with.


5Technically, a remote-tracking name is in a separate namespace. Our Git doesn't just tack origin/ in front, it replaces refs/heads/ with refs/remotes/origin/. The name origin is actually a remote and we can have as many remotes as we like in our Git repository. But this is a topic for another question.

这篇关于Git分支未显示所有分支的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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