Reputation: 11830
I am sort of afraid that my current working code doesn't get screwed.
So let's say I have following commit
7abd390c269d4abc9f3208d3c1b0a27ef5ae4162 -> Latest commit
5488da9335eb51eed274aa9b4a0f8f205643a8e0 -> 2nd commit
a31e310304404eb4452e6465e0a92dd472c5329a -> 1st commit
I checked out my first commit using
git checkout a31e310304404eb4452e6465e0a92dd472c5329a
And started doing changes in here. Now I want to commit this and push it to github.
but Things I am trying to avoid Git pull and Git Merge
Because at this point of time I have done so many changes.
Question: Can anyone tell me if it possible to force push this commit and If yes then how?
Upvotes: 0
Views: 1752
Reputation: 487883
What you probably want to do is to create a new branch name for your latest commit, e.g., git checkout -b newname
. You can then use this new name for all your work, or use it to remember your commits while you go back to older names and do work.
I checked out my first commit using
git checkout a31e310304404eb4452e6465e0a92dd472c5329a
Doing this produces what Git calls a detached HEAD
. While this may sound scary, it's actually not fatal, it just means that HEAD
is not attached.
Normally, the special name HEAD
(written in all capitals, just like this1) is attached to some existing branch name. A branch name, in Git, is how Git keeps track of who's doing what work. More specifically, the name itself stores the raw hash ID of these commits. So a branch name like master
or develop
would store that big ugly hash ID, the a31e310304404eb4452e6465e0a92dd472c5329a
thing. That lets you, the human, assign a meaningful name to this otherwise seemingly-random thing.
You're not doing the normal thing, and that means you really need to know what you're doing. Otherwise you'll lose track of your commits, and things will be painful.
1On Windows and MacOS, you can use head
in lowercase, but that's something of an an accident / quirk of the system. It's best to stick with the all-uppercase variant, since the lowercase one won't work on Linux. If you don't like typing out HEAD
in all caps, you can use @
instead, to mean the same thing.
Let's talk a bit more about how Git normally works with branch names, because this part is a little tricky, and yet, is one of the keys to using Git successfully. The first thing to understand is that Git itself does not care very much about these names: they are mostly for humans to use. Git mostly cares about the hash IDs, those big ugly strings of letters and numbers. Those uniquely identify each commit, and commits are the reason that Git exists at all. So they're what Git cares about.
Let's look at a really tiny, almost-new repository, with just three commits in it. These three commits will each have their own unique hash ID, but to save us a lot of headache, I'll use single uppercase letters for them instead. I'll call the first commit ever made A
, the second commit B
, and the third one C
.
Each commit stores a complete snapshot of a source tree, along with some metadata, which is just a fancy word for the stuff git log
can print about the commit: who made it and when, and what log message they entered for it. The metadata also includes the raw hash ID of the commit's parent commit, which is the commit that was in place when the person made that commit.
In other words, each commit points back to its previous commit. So let's draw that:
A <-B <-C
Commit A
, being the very first commit, has no parent, because it can't. But commit B
remembers that A
was the commit when B
was made, so B
points back to A
. Likewise, commit C
remembers for us that B
was its parent, so C
points back to B
.
What we—and Git—need at this point is a way to find the big ugly hash ID for commit C
. This is where branch names come in: the branch name master
can hold the big ugly unique hash ID for C
, like this:
A--B--C <-- master
We say that the branch name, master
, points to the last commit in the chain. We use the branch name to get the hash ID, and that lets us (via Git) get to commit C
. Commit has B
's hash inside it, so from C
we can find B
, and from B
, we can find A
. A
has no parent, so we can stop at this point.
Now, we if we only have the one branch name, it's pretty simple. We git checkout master
, which gets us commit C
. Then we make some changes, git add
our changed files, and run git commit
. This packages up the new snapshot, adds our own name and the current time, adds our log message, saves C
's hash ID, and makes a new commit, which now gets a new, unique, big ugly hash ID, and now the key step happens: Git writes the new hash ID into the name master
:
A--B--C--D <-- master
But what if we have two branch names? Let's create a new branch name now:
A--B--C--D <-- master, develop
Right now, both names store the same hash ID, the one for commit D
. Now Git needs to know which branch name to update. So to do that, Git attaches HEAD
to one of them, whichever one we give to git checkout
. For instance:
git checkout master
A--B--C--D <-- master (HEAD), develop
but:
git checkout develop
A--B--C--D <-- master, develop (HEAD)
Either way, we're starting from commit D
. The difference is which branch name changes when we make new commit E
:
A--B--C--D <-- master
\
E <-- develop (HEAD)
The thing to remember here is this: Whenever we add a new commit, Git makes the new commit have the previously-current commit as its parent. Then Git writes the new commit's ID into the branch name to which HEAD
is attached.
Note that the git checkout
step does two things:
HEAD
to that branch name.Here, the first point did not matter only because both master
and develop
pointed to the same commit.
If you git checkout
a commit by its raw hash ID, then Git still selects that commit to be the current commit, but this time, it doesn't attach HEAD
to a branch name. Let's say, for instance, that you pick the hash ID of commit C
. There is no branch name pointing to C
, so Git has to do this instead:
E <-- develop
/
D <-- master
/
A--B--C <-- HEAD
All we did here was to swing D
and E
up out of the way so that we can draw HEAD
in pointing directly to commit C
. What Git did here was to write the raw hash ID into the name HEAD
, rather than having the branch name in HEAD
. (If you want, you can look at the file .git/HEAD
: it either contains a raw hash ID like this, or, if it's attached to a branch, contains the text ref: refs/heads/branch
.)
Now we can make a new commit as usual, by editing files, running git add
as needed, and running git commit
. Git collects our commit message as usual, and makes a new commit as usual. The new commit's parent commit is the current commit—commit C
—whose hash ID is in the HEAD
file. Then, instead of writing the new commit's hash ID to the branch name in the HEAD
file, Git just writes the new commit's ID into HEAD
directly:
E <-- develop
/
D <-- master
/
A--B--C--F <-- HEAD
If you go on to make two more commits, the process simply repeats:
E <-- develop
/
D <-- master
/
A--B--C--F--G--H <-- HEAD
Note that Git can easily find commit H
right now, because its hash ID is in the HEAD
file. From H
, Git can find G
and F
(and then C
and so on too). But if you git checkout master
or git checkout develop
, for instance, Git will make commit D
or E
the current commit and will write the branch name into the HEAD
file, losing H
's hash ID.
If you save the hash ID somewhere, you can still work with it, for a while. Git tries not to throw out un-findable commits like this for a while.2 But it becomes hard to find, lost in a sea of nearly-identical commits distinguished mainly by their seemingly-random hash IDs. The best thing to do is to give it a name.
2This lasts at least 30 days by default, using the reflog for HEAD
. The reflog contains all the hash IDs that HEAD
named over the last 30 to 90 days, or longer, depending on a number of details that I won't go into here as this is already too long. :-)
What you probably want to do now, then, is to assign a name to remember the hash ID H
. This is what branch names—and tag names, for that matter—are for. If you create a new branch name right now, by default, it will point to the current commit:
git branch xyzzy
E <-- develop
/
D <-- master
/
A--B--C--F--G--H <-- HEAD, xyzzy
Note that this didn't attach HEAD
to the name xyzzy
. After creating the name, you can git checkout xyzzy
to tell Git to switch from commit H
, where HEAD
points now, to commit H
, where xyzzy
points. That switch does nothing, but now the second part of git checkout
applies, and Git attaches HEAD
to the name xyzzy
:
E <-- develop
/
D <-- master
/
A--B--C--F--G--H <-- xyzzy (HEAD)
You can do both of these at once using git checkout -b xyzzy
: that creates xyzzy
, pointing to the current commit, and at the same time, attaches HEAD
to the new name xyzzy
.
Once you have done this, you now have a name for your commits. It's now possible to use git push
to send the commits to another Git repository, telling that other Git repository to set its xyzzy
to point to commit H
(whatever H
's actual hash ID is).
When the other Git repository gets H
, it also gets G
and F
and, if required, C
and B
and even A
(assuming this particular graph drawing is correct). Essentially, git push
sends all the commits that are contained in—or reachable from (see Think Like (a) Git)—the branch, unless the other Git already has them. Then your Git has their Git set some name, usually the same name, to remember the last commit, the way branches do.
If you don't wish to save the two F
and G
snapshots and their commit metadata, it's now time to use git rebase
or similar to rewrite some commits, but that's a topic for another question (and there are lots of answers on StackOverflow already about this).
Upvotes: 3
Reputation: 22023
If you force push your new commit, you will loose the other 2 commits that are online (I assume that you ave a new local commit, no reason why not).
Create a new local branch if you don't want to merge just now. But you will "have to" pull what there is online, and then merge.
Pulling and merging won't destroy your local merge, but you have to merge if you stay on the same branch because there can be only one head commit per named branch with git.
Upvotes: 0