Reputation: 1324
I am fairly new to git. But currently, I have two branches under master:
refactor
and final
. I am trying to move and push all files from refactor
to final
however, after checking out to refactor
and rebasing to final
I get the final error:
fatal: Needed a single revision
invalid upstream final
Basically, I am trying to work on my code in refactor
and after testing to push it to master.
How can I do this?
Upvotes: 0
Views: 2101
Reputation: 489758
Starting from the end and working backwards—which, incidentally, is what Git does too—let's look at this:
invalid upstream final
This suggests you ran git rebase final
, but do not yet have a branch name final
.
I am trying to move and push all files from
refactor
tofinal
There's several important things to know here, starting with this: Git isn't really about files. We'll come back to that in a moment, and finish going backwards now:
But currently, I have two branches under master ...
Branches—more precisely, branch names—do not have any kind of over/under relationships. (This going-backwards stuff is kind of a pain, isn't it? But it's what Git does, so it's good exercise to do it for a bit. 😀)
Now, the thing to know about Git here is that it's all about commits. It's not about files or branches. Files and branches are important, of course, but as far as Git is concerned, it's the commits that matter. A commit holds files, and we find a commit using a branch name. But Git is all about commits. What you might like to do, then, is move the commits, but that might also be a problem. Here's what you need to know.
Since Git is about commits, you need to know exactly what a commit is and does for you. So you must memorize a few things:
Every commit is numbered. They're not counting numbers, like 1, 2, 3; but each commit has a unique number. The numbers look random, and are big and ugly and have letters in them too: 385c171a018f2747b329bcfa6be8eda1709e5abd
for instance. These numbers are actually cryptographic checksums of everything inside the commit. Git looks up the commit by this number.
Each commit has two parts, the data and the metadata:
The data in a commit is simply a snapshot of every file that Git knew about when you (or whoever) made the commit. These files are stored in a special, read-only, Git-only, compressed and de-duplicated form. The de-duplication handles the fact that most commits mostly just re-use all the files from the previous commit.
The metadata in a commit contains stuff like your name and email address, and any log message you want to put in. Git adds its own stuff to this metadata, though: every commit stores the commit-number—the hash ID—of the previous commit. Git calls this the parent of the commit.
Most of everything else in Git falls out of these facts:
Git doesn't store changes, but rather snapshots.
The commits get strung together, in backwards-looking chains:
... <-F <-G <-H
Here H
is the hash ID of the last commit in the chain. If we know the actual hash ID of commit H
, we can have Git look it up. That gets both the snapshot, and the actual hash ID of its parent commit G
. We can have Git look that up, which gets an earlier snapshot, and the hash ID of even-earlier commit F
, and so on.
Because the number of a commit is a cryptographic checksum, it's actually not possible to change anything about a commit. If you take one out and make some changes to it and store the result, you get a new and different commit, with a different hash ID. The old commit is still there, unchanged.
A branch name just holds the hash ID of the last commit in the chain.
When you do make a new commit, Git writes the new commit's ID into the name:
...--F--G--H--I <-- master
Here, we've added a new commit I
to master
. The existing commits didn't change: H
still points back to G
, which still points back to F
, and so on. All we did was add a new commit I
that points back to H
. When we did, since we were using master
, Git wrote I
's ID into the name master
.
So the names move, but the commits don't change at all.
When you use Git, you generally start with git checkout branch
. This copies the files out of one commit—remember, the branch name specifies one commit, and the files inside the commit are in a read-only, Git-only form—to where you can use them. It also tells Git that that name is your current name, and therefore that particular commit is your current commit. That's what these drawings:
...--G--H <-- master (HEAD)
are all about. The special name HEAD
is attached to a branch name, like master
. That's your current branch, and the commit to which this points is your current commit.
If you now make a new branch name, e.g., refactor
, you get this:
...--G--H <-- master (HEAD), refactor
Once you switch to the name refactor
, you have this:
...--G--H <-- master, refactor (HEAD)
Either way, you're still using commit H
. It's just that the name for commit H
is either master
or refactor
.
Now you make new commits, in the usual way. If you're on refactor
when you do that, this is what happens:
...--G--H <-- master
\
I--J <-- refactor (HEAD)
Here, you've made two new commits. These snapshots, J
being the last one, contain files and metadata. J
points back to I
, which points back to H
. The name refactor
selects commit J
and the name master
selects commit H
.
I [would] like to commit all changes from repository
refactor
to repositoryfinal
Hang on: you just said repository, but earlier, you said branch.
A repository is a collection of commits (with branch names, which help you and Git find the commits). A branch is ... well, it's ambiguous: people use the word to mean different things. But a branch name is a name that helps you and Git find one specific commit, from which Git works backwards as needed.
If you want a new branch name spelled final
that identifies commit J
, that's easy:
git branch final # makes `final` point to the current commit
or:
git branch final refactor # makes `final` point to the same commit as `refactor`
The end result might look like this—note that again, no commits have actually changed:
...--G--H <-- master
\
I--J <-- refactor (HEAD), final
Git has a bunch of technical terms, such as remote, repository, branch, and so on. Not everyone actually uses these terms the right way (and some of them are just not very good in the first place, and some have evolved over time). If you're trying to get a question across to a human, or to get Git to do something, it can be very important to use the right terms, or to add a bit of explanation if you're not sure about terminology.
Git has a glossary to help out here. It's definitely not perfect, but it's a starting point, at least.
Upvotes: 2