Posted By: Anonymous
I just deleted ALL the code from a file in my project and committed the change to my local git (on purpose). I did
git pull upstream master
to fetch and merge from upstream (so in theory that deleted code should be back).
Git tells me everything is up to date.
Everything is definitely NOT up to date — all that deleted code is still deleted.
Other Relevant Info
I only have one branch called “master”.
I recently set up “master” to track upstream like so:
Branch master set up to track remote branch master from upstream.
git branch -vv yields:
* master 7cfcb29 [upstream/master: ahead 9] deletion test
Why why why is this happening? I’m on the verge of just e-mailing my project manager any changes I make to our code.
I thought it was obvious, but anyways this is my goal:
Get the most recent of the code on my system.
Excuse my anger here, but why does such a simple task as that have to be so hard?
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
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).
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.
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.
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
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
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
--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"
git branch -m:
C0 - "remotes/upstream/master" - C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9 "bunchofhacks"
git branch -t master upstream/master:
C0 - "remotes/upstream/master", "master" - C1 --- C2 --- C3 --- C4 --- C5 --- C6 --- C7 --- C8 --- C9 "bunchofhacks"
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
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.)