简体   繁体   中英

git fetch remote branch, but cannot see in local repository or working copy

my current branch is staging_kei20201211, I want to fetch the latest code from origin/staging branch, I type the following command

git fetch origin staging

and it said获取输出

1) then I go to visual studio to see the history of my branch staging_kei20201211, but I cannot see the commit c03c99691 as stated in the fetch output, why?

  1. what is the meaning of the fetch output screen? From http://XXXXXX/gitea/elm-ha/CFMS
  • branch staging -> FETCH_HEAD c97e1dbb7..c03c99691 staging -> origin/staging

Note that fetching only updates the local tracking branches. In this case, your fetch updated the following branch:

origin/staging_kei20201211

To update the actual local branch staging_kei20201211 , you would to do the following additional step:

# from staging_kei20201211
git merge origin/staging_kei20201211

More typically though, you would checkout your local branch and do a git pull :

git checkout staging_kei20201211
git pull origin staging_kei20201211

Let's start with question 2 first, as it's useful in looking at question 1:

what is the meaning of the fetch output screen?

 From http://XXXXXX/gitea/elm-ha/CFMS * branch staging -> FETCH_HEAD c97e1dbb7..c03c99691 staging -> origin/staging

There's actually even more output above that, most of it prefixed with the word remote: . The remote: -prefixed text is output from commands that the server itself ran. This led to the server discovering 59 internal Git objects that their Git wanted to send to your Git, for your Git to get everything they have, that you did not have but were asking for, at this time. They then "packed" those objects, which actually compressed it further to just about 2 KiB to send, and then sent that; your Git then uncompressed this data, and put the internal objects into your repository.

At this point, you had all the new commits , but you had no way to find the new commits in your repository. The new commits were From http://... , so that's the From part. These commits were found in the other Git repository, by using the name staging in that other Git repository. So your Git wrote to an internal file named FETCH_HEAD the fact the branch name you gave to git fetch , ie, staging , corresponded to commit c03c99691 , which now exists in your repository (but, as yet, still has no name by which you can find it).

The last line tells you what your Git did in your repository to make it possible for you to find commit c03c99691 . Your own Git updated your name origin/staging . Your origin/staging used to find commit c97e1dbb7 . Now it finds commit c03c99691 . That's what:

 c97e1dbb7..c03c99691  staging -> origin/staging

means: that their staging became your origin/staging and that your origin/staging used to hold c97e1dbb7 , but now holds c03c99691 , the same as their staging .

The end result here is that your origin/staging , which is a remote-tracking name rather than a branch name, holds the same commit hash ID as their branch name staging . You can therefore use the name origin/staging to find the commit. You can also use the raw hash ID: c03c99691 .

Git is really all about commits

In Git, it's the commits that matter. Every commit has a unique number. The commit(s) you just got from them end with a commit whose unique number is c03c99691 . (This is actually a shortened form: the full hash ID is even bigger and uglier. Git shortens them down sometimes, keeping only the first few characters, to help avoid overwhelming mere humans.) This number is the same in every Git repository . Your repository uses this number, and so does theirs.

Each commit itself consists of two parts:

  • A commit holds a full snapshot of every file. The files inside the commit are stored in a special, compressed, read-only, Git-only, and de-duplicated format. That way, the fact that when you make a new commit, you're mostly re-using the files from the previous commit, the new commit doesn't take much space. Only a changed file requires a new internal object. For all the files that haven't changed, their content is the same as the file in some other commit, so they can share the de-duplicated parts.

  • Meanwhile, each commit also holds some information about the commit itself, such as who made it, when, and why. In this information inside the commit, each commit holds a list of the raw hash IDs of any earlier commits used to make this commit. Usually there's just one such hash ID, which we call the parent of the commit.

This parent hash ID trick is what makes Git work. Suppose we have a series of commits, all in a nice simple row with no branching and merging going on. The last commit in this sequence has some big ugly hash ID, but we'll just call it commit H . Commit H has, inside itself, the big ugly hash ID of its parent commit; we'll call that commit G .

We say that the child commit points to the parent, and we can draw that:

        <-G <-H

You can see how there is an arrow coming out of H , pointing backwards to G . There's an arrow coming out of G too, of course. It points backwards to the commit before G , which we'll call F :

... <-F <-G <-H

As before, F also points backwards. This backwards-looking chain lets us—or Git—start at the last commit and find the history. The history in a repository is nothing more or less than the commits in the repository. That's all there is, but that's all there needs to be. Each commit points backwards, to the earlier commits.

Branch names and other names change; hash IDs remain the same

This is where branch names enter our picture. In order to find all the commits, we need the hash ID of the last commit. Above, that was commit H . So we put the hash ID of the last commit into a name, such as a branch name. If the branch name master contains the hash ID of commit G , and the branch name staging_kei20201211 contains the hash ID of commit H , we can draw it like this:

...--F--G   <-- master
         \
          H   <-- staging_kei20201211

Here, commit H points back to earlier commit G . The name master also points to commit G . This means that commits up through G are on both branches , while commit H is only on staging_kei20201211 .

(Whether this is the case in your repository, I have no idea. Note that we've also used symbolic names, G and H , for commits; their real names are big ugly hash IDs. To find the hash IDs, use git rev-parse :

git rev-parse master

will tell you the real hash ID of the commit that master points to, for instance.)

With this in mind, let's look at what happens when you add a new commit to some branch. Let's start with git switch master or git checkout master , so that the current branch name is master and the current commit is commit G :

...--F--G   <-- master (HEAD)
         \
          H   <-- staging_kei20201211

The difference between this drawing and the previous one is that we attached the special name HEAD to the name master , to tell us which branch name is the current branch. (The git branch command would now print this name in green, instead of white, as you see with your staging_kei20201211 .)

We could now make a new name that also points to G , and switch to it, with:

git switch -C temp-branch

to get:

...--F--G   <-- master, temp-branch (HEAD)
         \
          H   <-- staging_kei20201211

If we now make a new commit in the usual way (modify files, git add , and git commit ), we'll get a new commit, with a new, unique hash ID. This big ugly hash ID will be one that is not in any other Git repository anywhere (which is why they have to be as big and ugly as they are), but we'll just call it commit I , and draw it in like this:

          I   <-- temp-branch (HEAD)
         /
...--F--G   <-- master
         \
          H   <-- staging_kei20201211

Note how the name temp-branch has changed: it now points to the new commit . The old commit is still there, and commits up through G are now on all three branches. The name has moved, but the commits stayed in place. We just added one new commit, which is now the last commit on branch temp-branch .

If we check out some other branch name, such as staging_kei20201211 , and delete the name temp-branch , we get:

          I   ???
         /
...--F--G   <-- master
         \
          H   <-- staging_kei20201211 (HEAD)

Commit I still exists , but if you did not save its commit hash ID anywhere, it will be hard to find. Git will hang on to the commit for some time, in case you want it back, but you'll have to find its hash ID. If you don't grab it back somehow, Git will eventually discard commit I entirely. (That's how we make, then abandon, temporary commits if we want to do that.)

Git's fetch and push deal in hash IDs to transfer commits

While we find commits by name, the actual commits themselves are identified by hash IDs. To see if we already have some commit, when we hook two Gits up to each other, they just exchange the raw hash IDs. Since these are unique across every Git, one Git can tell if another one has the same commit by whether it has a commit with the same hash ID. If not, the sending Git just sends it over. All Gits number commits in the same way, 1 so the receiving Git will use the same random-looking number for it.


1 To make this work, Git uses a cryptographically strong hash function. The "cryptographically strong" part is not essential to Git itself but is useful for other purposes. The current algorithm, SHA-1, is not quite strong enough any more and Git is moving to a new hash algorithm, but this is a long term changeover, with many problems foreseen; as Git starts doing the changeover, the unforeseen ones will turn up.


Once obtained, the receiving Git needs to have a name

In your case, you ran git fetch to a Git whose contact URL is remembered under the name origin . Your Git called up the server at that URL. That server invoked a Git connected to a repository in which there was a branch name staging , which held hash ID c03c99691 . For simplicity, I'm going to call this commit K .

Your Git had talked to this other Git before. In fact, given the name origin , your Git probably got started by copying all the commits that this other Git repository had* into your own new Git repository. So this commit got added to origin since then. They moved their branch name staging . They probably had:

...--G   <-- master
      \
       J   <-- staging

when you did your original git clone , so that you, in your repository, have, say:

       H   <-- staging_kei20201211 (HEAD)
      /
...--G   <-- origin/master
      \
       J   <-- staging, origin/staging

(or perhaps a different graph with a different structure, but from your git branch names and your git fetch output, I suspect you have an origin/master , and I know you have an origin/staging ). Your origin/* names result from your Git copying their Git repository's branch names.

Anyway, they now have some new commit(s). I'm going to guess that they have exactly one new commit and your git fetch brought that one in:

       H   <-- staging_kei20201211 (HEAD)
      /
...--G   <-- origin/master
      \
       J   <-- staging
        \
         K   <-- origin/staging

If this drawing is accurate, J is really c97e1dbb7 and K is really c03c99691 .

Now to question 1

I go to visual studio to see the history of my branch staging_kei20201211, but I cannot see the commit c03c99691 as stated in the fetch output, why?

I do not know or use Visual Studio, but in general, to get any Git viewer to show you some commit(s), you must tell them either the raw hash ID of the last such commit, or give them a name that allows them to use Git to find the last such commit.

As you now know, the name origin/staging will, by definition, find the last commit in the chain ending at that commit. That's because your Git updated your origin/staging to match the name staging in the Git repository that the other Git was looking at.

From a bash-style command line:

$ git log origin/staging

would show you some commits, starting with c03c99691 as found by origin/staging . The command:

$ git show origin/staging

would find commit c03c99691 , find its parent—probably c97e... , but perhaps some other commit if you got two or more commits from origin just now—and compare the snapshots in the two commits . For each file that is exactly the same, git show would show nothing. For each file that is different, git show would show you what changed. All of this would be prefixed with the log message in commit c03c99691 , plus the name and email address and date-and-time-stamp information stored in commit c03c99691 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM