148

基本问题

我刚刚从我的项目中的一个文件中删除了所有代码,并将更改提交给我的本地 git(故意)。我做了

git pull upstream master

从上游获取和合并(因此理论上应该返回已删除的代码)。

Git 告诉我一切都是最新的。

一切都绝对不是最新的——所有被删除的代码仍然被删除。

其他相关信息

我只有一个名为“master”的分支。

我最近设置了“master”来跟踪上游,如下所示:

分支主机设置为从上游跟踪远程分支主机。

该命令git branch -vv产生:

* master 7cfcb29 [upstream/master: ahead 9] deletion test

为什么为什么会发生这种情况?我即将通过电子邮件向我的项目经理发送我对代码所做的任何更改。

更新

我认为这很明显,但无论如何这是我的目标:

获取我系统上的最新代码。

在这里原谅我的愤怒,但是为什么这么简单的任务必须如此困难?

4

7 回答 7

245

I think your basic issue here is that you're misinterpreting and/or misunderstanding what git does and why it does it.

When you clone some other repository, git makes a copy of whatever is "over there". It also takes "their" branch labels, such as master, and makes a copy of that label whose "full name" in your git tree is (normally) remotes/origin/master (but in your case, remotes/upstream/master). Most of the time you get to omit the remotes/ part too, so you can refer to that original copy as upstream/master.

If you now make and commit some change(s) to some file(s), you're the only one with those changes. Meanwhile other people may use the original repository (from which you made your clone) to make other clones and change those clones. They are the only ones with their changes, of course. Eventually though, someone may have changes they send back to the original owner (via "push" or patches or whatever).

The git pull command is mostly just shorthand for git fetch followed by git merge. This is important because it means you need to understand what those two operations actually do.

The git fetch command says to go back to wherever you cloned from (or have otherwise set up as a place to fetch from) and find "new stuff someone else added or changed or removed". Those changes are copied over and applied to your copy of what you got from them earlier. They are not applied to your own work, only to theirs.

The git merge command is more complicated and is where you are going awry. What it does, oversimplified a bit, is compare "what you changed in your copy" to "changes you fetched from someone-else and thus got added to your-copy-of-the-someone-else's-work". If your changes and their changes don't seem to conflict, the merge operation mushes them together and gives you a "merge commit" that ties your development and their development together (though there is a very common "easy" case in which you have no changes and you get a "fast forward").

The situation you're encountering now is one in which you have made changes and committed them—nine times, in fact, hence the "ahead 9"—and they have made no changes. So, fetch dutifully fetches nothing, and then merge takes their lack-of-changes and also does nothing.

What you want is to look at, or maybe even "reset" to, "their" version of the code.

If you merely want to look at it, you can simply check out that version:

git checkout upstream/master

That tells git that you want to move the current directory to the branch whose full name is actually remotes/upstream/master. You'll see their code as of the last time you ran git fetch and got their latest code.

If you want to abandon all your own changes, what you need to do is change git's idea of which revision your label, master, should name. Currently it names your most recent commit. If you get back onto that branch:

git checkout master

then the git reset command will allow you to "move the label", as it were. The only remaining problem (assuming you're really ready to abandon everything you've don) is finding where the label should point.

git log will let you find the numeric names—those things like 7cfcb29—which are permanent (never changing) names, and there are a ridiculous number of other ways to name them, but in this case you just want the name upstream/master.

To move the label, wiping out your own changes (any that you have committed are actually recoverable for quite a while but it's a lot harder after this so be very sure):

git reset --hard upstream/master

The --hard tells git to wipe out what you have been doing, move the current branch label, and then check out the given commit.

It's not super-common to really want to git reset --hard and wipe out a bunch of work. A safer method (making it a lot easier to recover that work if you decide some of it was worthwhile after all) is to rename your existing branch:

git branch -m master bunchofhacks

and then make a new local branch named master that "tracks" (I don't really like this term as I think it confuses people but that's the git term :-) ) the origin (or upstream) master:

git branch -t master upstream/master

which you can then get yourself on with:

git checkout master

What the last three commands do (there's shortcuts to make it just two commands) is to change the name pasted on the existing label, then make a new label, then switch to it:

before doing anything:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "master"

after git branch -m:

C0 -    "remotes/upstream/master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

after git branch -t master upstream/master:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9    "bunchofhacks"

Here C0 is the latest commit (a complete source tree) that you got when you first did your git clone. C1 through C9 are your commits.

Note that if you were to git checkout bunchofhacks and then git reset --hard HEAD^^, this would change the last picture to:

C0 -    "remotes/upstream/master", "master"
    \
     \- C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 -    "bunchofhacks"
                                                      \
                                                       \- C8 --- C9

The reason is that HEAD^^ names the revision two up from the head of the current branch (which just before the reset would be bunchofhacks), and reset --hard then moves the label. Commits C8 and C9 are now mostly invisible (you can use things like the reflog and git fsck to find them but it's no longer trivial). Your labels are yours to move however you like. The fetch command takes care of the ones that start with remotes/. It's conventional to match "yours" with "theirs" (so if they have a remotes/origin/mauve you'd name yours mauve too), but you can type in "theirs" whenever you want to name/see commits you got "from them". (Remember that "one commit" is an entire source tree. You can pick out one specific file from one commit, with git show for instance, if and when you want that.)

于 2013-03-13T09:28:16.237 回答
40

I had the same problem as you.

I did git status git fetch git pull, but my branch was still behind to origin. I had folders and files pushed to remote and I saw the files on the web, but on my local they were missing.

Finally, these commands updated all the files and folders on my local:

git fetch --all
git reset --hard origin/master 

or if you want a branch

git checkout your_branch_name_here
git reset --hard origin/your_branch_name_here
于 2019-01-02T12:39:52.667 回答
4

您提交的任何更改,例如删除所有项目文件,在拉取后仍然存在。拉取所做的只是将其他地方的最新更改合并到您自己的分支中,如果您的分支删除了所有内容,那么当上游更改影响您已删除的文件时,您最多会遇到合并冲突。所以,简而言之,是的,一切都是最新的。

如果您描述您想要的结果而不是“删除所有文件”,也许有人可以建议适当的行动方案。

更新:

在我的系统上获取最新的代码

What you don't seem to understand is that you already have the most recent code, which is yours. If what you really want is to see the most recent of someone else's work that's on the master branch, just do:

git fetch upstream
git checkout upstream/master

Note that this won't leave you in a position to immediately (re)start your own work. If you need to know how to undo something you've done or otherwise revert changes you or someone else have made, then please provide details. Also, consider reading up on what version control is for, since you seem to misunderstand its basic purpose.

于 2013-03-13T03:02:22.827 回答
4

While none of these answers worked for me, I was able to fix the issue using the following command.

git fetch origin

This did a trick for me.

于 2019-11-14T12:57:12.357 回答
3

As the other posters say, pull merges changes from upstream into your repository. If you want to replace what is in your repository with what is in upstream, you have several options. Off the cuff, I'd go with

git checkout HEAD^1  # Get off your repo's master.. doesn't matter where you go, so just go back one commit
git branch -d master  # Delete your repo's master branch
git checkout -t upstream/master  # Check out upstream's master into a local tracking branch of the same name
于 2013-03-13T03:20:08.280 回答
1

The top answer is much better in terms of breadth and depth of information given, but it seems like if you wanted your problem fixed almost immediately, and don't mind trodding on some of the basic principles of version control, you could ...

  1. Switch to master

    $ git checkout upstream master
    
  2. Delete your unwanted branch. (Note: it must be have the -D, instead of the normal -d flag because your branch is many commits ahead of the master.)

    $ git branch -d <branch_name>
    
  3. Create a new branch

    $ git checkout -b <new_branch_name>
    
于 2017-03-05T00:30:02.607 回答
0

Just a friendly reminder if you have files locally that aren't in github and yet your git status says

Your branch is up to date with 'origin/master'. nothing to commit, working tree clean

It can happen if the files are in .gitignore

Try running

cat .gitignore 

and seeing if these files show up there. That would explain why git doesn't want to move them to the remote.

于 2020-08-01T14:19:54.920 回答