Xcode won't commit to master branch after Cherry-Picking [duplicate] - xcode

I was doing some work in my repository and noticed a file had local changes. I didn't want them anymore so I deleted the file, thinking I can just checkout a fresh copy. I wanted to do the Git equivalent of
svn up .
Using git pull didn't seem to work. Some random searching led me to a site where someone recommended doing
git checkout HEAD^ src/
(src is the directory containing the deleted file).
Now I find out I have a detached head. I have no idea what that is. How can I undo?

Detached head means you are no longer on a branch, you have checked out a single commit in the history (in this case the commit previous to HEAD, i.e. HEAD^).
If you want to delete your changes associated with the detached HEAD
You only need to checkout the branch you were on, e.g.
git checkout master
Next time you have changed a file and want to restore it to the state it is in the index, don't delete the file first, just do
git checkout -- path/to/foo
This will restore the file foo to the state it is in the index.
If you want to keep your changes associated with the detached HEAD
Run git branch tmp - this will save your changes in a new branch called tmp.
Run git checkout master
If you would like to incorporate the changes you made into master, run git merge tmp from the master branch. You should be on the master branch after running git checkout master.

If you have changed files you don't want to lose, you can push them. I have committed them in the detached mode and after that you can move to a temporary branch to integrate later in master.
git commit -m "....."
git branch my-temporary-work
git checkout master
git merge my-temporary-work
Extracted from:
What to do with commit made in a detached head

A solution without creating a temporary branch.
How to exit (“fix”) detached HEAD state when you already changed something in this mode and, optionally, want to save your changes:
Commit changes you want to keep. If you want to take over any of the changes you made in detached HEAD state, commit them. Like:
git commit -a -m "your commit message"
Discard changes you do not want to keep. The hard reset will discard any uncommitted changes that you made in detached HEAD state:
git reset --hard
(Without this, step 3 would fail, complaining about modified uncommitted files in the detached HEAD.)
Check out your branch. Exit detached HEAD state by checking out the branch you worked on before, for example:
git checkout master
Take over your commits. You can now take over the commits you made in detached HEAD state by cherry-picking, as shown in my answer to another question.
git reflog
git cherry-pick <hash1> <hash2> <hash3> …

Detached head means:
You are no longer on a branch,
You have checked out a single commit in the history
If you have no changes: you can switch to master by applying the following command
git checkout master
If you have changes that you want to keep:
In case of a detached HEAD, commits work like normal, except no named branch gets updated. To get master branch updated with your committed changes, make a temporary branch where you are (this way the temporary branch will have all the committed changes you have made in the detached HEAD), then switch to the master branch and merge the temporary branch with the master.
git branch temp
git checkout master
git merge temp

HEAD is a pointer, and it points — directly or indirectly — to a particular commit:
Attached  HEAD means that it is attached to some branch (i.e. it points to a branch).
Detached HEAD means that it is not attached to any branch, i.e. it points directly to some commit.
In other words:
If it points to a commit directly, the HEAD is detached.
If it points to a commit indirectly, (i.e. it points to a branch, which in turn points to a commit), the HEAD is attached.
To better understand situations with attached / detached HEAD, let's show the steps leading to the quadruplet of pictures above.
We begin with the same state of the repository (pictures in all quadrants are the same):
Now we want to perform git checkout — with different targets in the individual pictures (commands on top of them are dimmed to emphasize that we are only going to apply those commands):
This is the situation after performing those commands:
As you can see, the HEAD points to the target of the git checkout command — to a branch (first 3 images of the quadruplet), or (directly) to a commit (the last image of the quadruplet).
The content of the working directory is changed, too, to be in accordance with the appropriate commit (snapshot), i.e. with the commit pointed (directly or indirectly) by the HEAD.
So now we are in the same situation as in the start of this answer:

If you made changes and then realized that you are on a detached head, you can do: stash -> checkout master -> stash pop:
git stash
git checkout master # Fix the detached head state
git stash pop # Or for extra safety use 'stash apply' then later
# after fixing everything do 'stash drop'
You will have your uncommited changes and normal "attached" HEAD, like nothing happened.

Here's what I just did after I realized I was on a detached head and had already made some changes.
I committed the changes.
$ git commit -m "..."
[detached HEAD 1fe56ad] ...
I remembered the hash (1fe56ad) of the commit. Then I checked out the branch I should have been on.
$ git checkout master
Switched to branch 'master'
Finally I applied the changes of the commit to the branch.
$ git cherry-pick 1fe56ad
[master 0b05f1e] ...
I think this is a bit easier than creating a temporary branch.

When you check out a specific commit in git, you end up in a detached head state...that is, your working copy no longer reflects the state of a named reference (like "master"). This is useful for examining the past state of the repository, but not what you want if you're actually trying to revert changes.
If you have made changes to a particular file and you simply want to discard them, you can use the checkout command like this:
git checkout myfile
This will discard any uncommitted changes and revert the file to whatever state it has in the head of your current branch. If you want to discard changes that you have already committed, you may want to use the reset command. For example, this will reset the repository to the state of the previous commit, discarding any subsequent changes:
git reset --hard HEAD^
However, if you are sharing the repository with other people, a git reset can be disruptive (because it erases a portion of the repository history). If you have already shared changes with other people, you generally want to look at git revert instead, which generates an "anticommit" -- that is, it creates a new commit that "undoes" the changes in question.
The Git Book has more details.

Since "detached head state" has you on a temp branch, just use git checkout - which puts you on the last branch you were on.

you probably did git reset --hard origin/your-branch.
Try to just git checkout your-branch

Being in "detached head" means that HEAD refers to a specific unnamed commit (as opposite to a named branch) (cf: https://git-scm.com/docs/git-checkout section Detached head).
In reality, this means that you have checked out a commit but there is no branch name associated with it.
You may choose to only create a new branch associated with your commit by
git branch new-branch-name.
This allows you to save your current state in a new branch named new-branch-name and not be in a detached head state anymore.
Or if you would like to come back to the previous state, you need to select the branch that was selected before by
git checkout #{-1}

To further clarify #Philippe Gerber's answer, here it is:
Before cherry-pick, a git checkout master is necessary in this case. Furthermore, it is only needed with a commit in detached head.

Addendum
If the branch to which you wish to return was the last checkout that you had made, you can simply use checkout #{-1}. This will take you back to your previous checkout.
Further, you can alias this command with, for example, git global --config alias.prev so that you just need to type git prev to toggle back to the previous checkout.

Detached head means you have not checked out your branch properly or you have just checked out a single commit.
If you encounter such an issue then first stash your local changes so that you won't lose your changes.
After that... checkout your desired branch using the command:
Let's say you want branch MyOriginalBranch:
git checkout -b someName origin/MyOriginalBranch

To add to #ralphtheninja's answer. If you get this message after using git checkout master:
Please commit your changes or stash them before you switch branches.
Aborting
Then you can simply force the checkout using the -f flag as follows:
git checkout -f master
Apparently this will result in losing all the changes made in the detached mode. So be careful when using it.

This approach will potentially discard part of the commit history, but it is easier in case the merge of the old master branch and the current status is tricky, or you simply do not mind losing part of the commit history.
To simply keep things as currently are, without merging, turning the current detached HEAD into the master branch:
Manually back up the repository, in case things go unexpectedly wrong.
Commit the last changes you would like to keep.
Create a temporary branch (let's name it detached-head) that will contain the files in their current status:
git checkout -b detached-head
(a) Delete the master branch if you do not need to keep it
git branch -D master
(b) OR rename if you want to keep it
git branch -M master old-master
Rename the temporary branch as the new master branch
git branch -M detached-head master
Credit: adapted from this Medium article by Gary Lai.

Git told me how to do it.
if you typed:
git checkout <some-commit_number>
Save the status
git add .
git commit -m "some message"
Then:
git push origin HEAD:<name-of-remote-branch>

I was in a similar situation.
For some reason I ended up with a detached head - I had made commits on the same path as the branch I thought I was on - eg HEAD was a child of the branch tag but for some reason the branch tag had stayed back at a historic commit... possibly because I had pushed??
It wouldn't let me push because I wasn't considered to be on the branch I thought I was on.
I didn't want to change any of my history or do any cherry picking and I'd just spent about 8 weeks working on the branch so reset --hard was making me a bit nervous!
The solution was just to do the following:
git branch -f myStuckBranch HEAD
git checkout myStuckBranch
You need to do the checkout even though HEAD and myStuckBranch are now pointing at the same thing because you are still considered to be in the detached head state (not on a branch)
I'm not an expert with git (having mostly used mercurial which would never create this weird situation) but my understanding of this command is that it just says
"change myStuckBranch to point at HEAD".
I routinely find myself using this command to merge in changes from master after fetching without having to swap my working directory - otherwise it tries to use the old (uninteresting) version of master:
git fetch
git branch -f master origin/master -- err yeah don't just ignore what's been going on remotely - eg point my master at the real master
git merge master -- merge the changes into my local branch
It's a bit annoying to have to manually have to do that all the time but still better than having to change your working directory just to update another branch in order to merge in changes from it.

Normally HEAD points to a branch. When it is not pointing to a branch instead when it points to a commit hash like 69e51 it means you have a detached HEAD. You need to point it two a branch to fix the issue. You can do two things to fix it.
git checkout other_branch // Not possible when you need the code in that commit hash
create a new branch and point the commit hash to the newly created branch.
HEAD must point to a branch, not a commit hash is the golden rule.

This was a confusing thing to me when I started to work with git and later I figure out why this is happening and what is the best way to deal with such a situation.
The root cause for such occurrence is that normally git HEAD is always pointing to some branch and when you try to point the HEAD to some specific commit, you put HEAD into a detached HEAD state.
When HEAD is attached state -
cat .git/HEAD # output--> ref: refs/heads/master or ref: refs/heads/main
When HEAD is detached state -
cat .git/HEAD # output--> b96660a90cad75867453ebe1b8d11754bbb68b0e <commit hash>
Solution -
git stash # Temporarily shelves (or stashes) changes
git branch # Find your default branch
git switch master # Point HEAD to master or main branch
git stash pop # Apply all the changes you had previously

I wanted to keep my changes so, I just fix this doing...
git add .
git commit -m "Title" -m "Description"
(so i have a commit now example: 123abc)
git checkout YOURCURRENTBRANCH
git merge 123abc
git push TOYOURCURRENTBRANCH
that work for me

When you're in a detached head situation and created new files, first make sure that these new files are added to the index, for example with:
git add .
But if you've only changed or deleted existing files, you can add (-a) and commit with a message (-m) at the the same time via:
git commit -a -m "my adjustment message"
Then you can simply create a new branch with your current state with:
git checkout -b new_branch_name
You'll have a new branch and all your adjustments will be there in that new branch. You can then continue to push to the remote and/or checkout/pull/merge as you please.

Realizing I had a detached head without knowing how I managed to get it (like three commits away), I also found out that trying to merge, rebase or cherry-pick triggered hundreds of merge-conflicts, so I took a different approach:
(Assuming everything is committed (working tree is "clean"))
Save my commit messages: git log > /tmp/log
Save my working tree: mkdir /tmp/backup && cp -a all_my files_and_directories /tmp/backup
Revert to master: git checkout master
Remove all the working files and directories: rm ...
Use the backup: cp -a /tmp/backup/. .
git add and git commit using messages from saved /tmp/log, maybe repeating it with different sub-sets of files...
The disadvantage is that you loose your commit history if one file was changed multiple times since master, but in the end I had a clean master.

Git : You are not currently on a branch.
Time to time Git shows :
To push the history leading to the current (detached HEAD)
state now, use
git push origin HEAD:<name-of-remote-branch>
It means :
" HEAD have no branch "
To fix that run 2 commands :
git branch -f {{your_working_branch}} HEAD -- set branch to your head
git checkout {{your_working_branch}} -- checkout==switch branch

The detached HEAD means that you are currently not on any branch. If you want to KEEP your current changes and simply create a new branch, this is what you do:
git commit -m "your commit message"
git checkout -b new_branch
Afterwards, you potentially want to merge this new branch with other branches. Always helpful is the git "a dog" command:
git log --all --decorate --oneline --graph

git pull origin master
worked for me. It was just about giving remote and branch name explicitly.

This works for me, It will assign a new branch for detached head :
git checkout new_branch_name detached_head_garbage_name

With git rebase you can move your HEAD to the desired commit
Suppose you have your branch in a detached state, like this:
* bfcb8f9 Commit 4
* 540a123 Commit 3
* 4356d64 Commit 2
| * fecb8d2 Commit 2
|/
| * 8012f45 Commit 2x
|/
| * 6676d15 (HEAD -> master) Commit 2 --amend
|/
* 1818f91 Commit 1
The detached head was created by rebasing by mistake, pointing to a detached commit, which was created previously due a git commit --amend command.
If you want to move your HEAD ref to the most recent commit, apply a rebase with the desired HASH commit you want to point to. In this example, the hash is of the most recent commit:
git rebase bfcb8f9
and this will leave your branch with its HEAD poiting to the desired commit (the most recent in this case):
* bfcb8f9 (HEAD -> master) Commit 4
* 540a123 Commit 3
* 4356d64 Commit 2 --amend
| * fecb8d2 Commit 2
|/
| * 8012f45 Commit 2x
|/
| * 6676d15 Commit 2
|/
* 1818f91 Commit 1

If you hate head and wanna go back to main instead:
git checkout main
If you love head but just wish main tracked it:
git checkout -B main HEAD
(This works with any branch name, not just main, and for any commit pointer, not just HEAD.)

Related

Xcode 14 "switched" to older commit, how to get back more recent commits? [duplicate]

In Git, I was trying to do a squash commit by merging in another branch and then resetting HEAD to the previous place via:
git reset origin/master
But I need to step out of this. How can I move HEAD back to the previous location?
I have the SHA-1 fragment (23b6772) of the commit that I need to move it to. How can I get back to this commit?
Before answering, let's add some background, explaining what this HEAD is.
First of all what is HEAD?
HEAD is simply a reference to the current commit (latest) on the current branch.
There can only be a single HEAD at any given time (excluding git worktree).
The content of HEAD is stored inside .git/HEAD and it contains the 40 bytes SHA-1 of the current commit.
detached HEAD
If you are not on the latest commit - meaning that HEAD is pointing to a prior commit in history it's called detached HEAD.
On the command line, it will look like this - SHA-1 instead of the branch name since the HEAD is not pointing to the tip of the current branch:
A few options on how to recover from a detached HEAD:
git checkout
git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits to go back
This will checkout the new branch pointing to the desired commit.
This command will checkout to a given commit.
At this point, you can create a branch and start to work from this point on.
# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>
# Create a new branch forked to the given commit
git checkout -b <branch name>
git reflog
You can always use the reflog as well.
git reflog will display any change which updated the HEAD and checking out the desired reflog entry will set the HEAD back to this commit.
Every time the HEAD is modified there will be a new entry in the reflog
git reflog
git checkout HEAD#{...}
This will get you back to your desired commit
git reset --hard <commit_id>
"Move" your HEAD back to the desired commit.
# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32
# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.
Note: (Since Git 2.7) you can also use the git rebase --no-autostash as well.
git revert <sha-1>
"Undo" the given commit or commit range.
The revert command will "undo" any changes made in the given commit.
A new commit with the undo patch will be committed while the original commit will remain in history as well.
# Add a new commit with the undo of the original one.
# The <sha-1> can be any commit(s) or commit range
git revert <sha-1>
This schema illustrates which command does what.
As you can see there, reset && checkout modify the HEAD.
First reset locally:
git reset 23b6772
To see if you're on the right position, verify with:
git status
You will see something like:
On branch master Your branch is behind 'origin/master' by 17 commits,
and can be fast-forwarded.
Then rewrite history on your remote tracking branch to reflect the change:
git push --force-with-lease // a useful command #oktober mentions in comments
Using --force-with-lease instead of --force will raise an error if others have meanwhile committed to the remote branch, in which case you should fetch first. More info in this article.
Quickest possible solution (just 1 step)
Use git checkout -
You will see Switched to branch <branch_name>. Confirm it's the branch you want.
Brief explanation: this command will move HEAD back to its last position. See note on outcomes at the end of this answer.
Mnemonic: this approach is a lot like using cd - to return to your previously visited directory. Syntax and the applicable cases are a pretty good match (e.g. it's useful when you actually want HEAD to return to where it was).
More methodical solution (2-steps, but memorable)
The quick approach solves the OP's question. But what if your situation is slightly different: say you have restarted Bash then found yourself with HEAD detached. In that case, here are 2 simple, easily remembered steps.
1. Pick the branch you need
Use git branch -v
You see a list of existing local branches. Grab the branch name that suits your needs.
2. Move HEAD to it
Use git checkout <branch_name>
You will see Switched to branch <branch_name>. Success!
Outcomes
With either method, you can now continue adding and committing your work as before: your next changes will be tracked on <branch_name>.
Note that both git checkout - and git checkout <branch_name> will give additional instructions if you have committed changes while HEAD was detached.
The question can be read as:
I was in detached-state with HEAD at 23b6772 and typed git reset origin/master (because I wanted to squash). Now I've changed my mind, how do I go back to HEAD being at 23b6772?
The straightforward answer being: git reset 23b6772
But I hit this question because I got sick of typing (copy & pasting) commit hashes or its abbreviation each time I wanted to reference the previous HEAD and was Googling to see if there were any kind of shorthand.
It turns out there is!
git reset - (or in my case git cherry-pick -)
Which incidentally was the same as cd - to return to the previous current directory in *nix! So hurrah, I learned two things with one stone.
When you run the command git checkout commit_id then HEAD detached from 13ca5593d(say commit-id) and branch will be on longer available.
Move back to previous location run the command step wise -
git pull origin branch_name (say master)
git checkout branch_name
git pull origin branch_name
You will be back to the previous location with an updated commit from the remote repository.
Today, I mistakenly checked out on a commit and started working on it, making some commits on a detach HEAD state. Then I pushed to the remote branch using the following command:
git push origin HEAD: <My-remote-branch>
Then
git checkout <My-remote-branch>
Then
git pull
I finally got my all changes in my branch that I made in detach HEAD.
This may not be a technical solution, but it works. (if anyone of your teammate has the same branch in local)
Let's assume your branch name as branch-xxx.
Steps to Solve:
Don't do update or pull - nothing
Just create a new branch (branch-yyy) from branch-xxx on his machine
That's all, all your existing changes will be in this new branch (branch-yyy). You can continue your work with this branch.
Note: Again, this is not a technical solution, but it will help for sure.
Move last non-pushed commits to a new branch
If your problem is that you started committing on the WRONG_BRANCH, and want to move those last non-pushed commits to the RIGHT_BRANCH, the easiest thing to do is
git checkout WRONG_BRANCH
git branch RIGHT_BRANCH
git reset —-hard LAST_PUSHED_COMMIT
git checkout RIGHT_BRANCH
At this point, if you run git log HEAD you will see that all your commits are there, in the RIGHT_BRACH.
Data
WRONG_BRANCH is where your committed changes (yet to push) are now
RIGHT_BRANCH is where your committed changes (yet to push) will be
LAST_PUSHED_COMMIT is where you want to restore the WRONG_BRANCH to

Store several different .git branches in the same directory?

There was no local .git repository on Windows in a "..Downloads/Training" folder, using per-installed git bash, I first typed
git init
touch .gitignore in acccordance with the youtube tutorial (see link listed below) copied .gitignore file contents from previously created by visual studio repository, also added .gitignore by
git add .gitignore , also added the only subfolder M01 in "..Downloads/Training" by typing
git add ., commited the changes by
git commit -am "First commit, added the remote by typing
git remote add origin https:name_of_remote.com/my_repository_folder, created a branch
git branch M01, switched to the branch by
git switch M01, also pushed the repo by typing
git push origin HEAD:M01.
And the repository has been pushed successfully into that remote but now there is a problem:
I need to store the contents of each folder inside "..Downloads/Training" in a separate branch on the remote.
So if I create a new local folder M02 and a branch by typing git branch M02, switching to it by git switch M02, It shows me all of the contents that I have previously added into the M01 branch in the M02 branch, but If I remove the files from M02 by typing git rm . -r (it deletes local files), it also deletes the files from both M01 branch and M02 branch.
Is there a way to store only M01 local folder in a M01 branch and the M02 local folder in a M02 branch?
Additional source tutorial links:
(https://www.youtube.com/watch?v=g4BJXfmAevA)
You have learned some things that are wrong. Note: I have not watched the particular youtube tutorial you linked, so I'm not sure if it is good, bad, or indifferent. I base this paragraph solely on what you wrote in your question.
First, let's address what a Git repository is and is not:
A Git repository is a collection of commits. These commits are to be found using branch names, tag names, and other names, but it's not a collection of branches. It's a collection of commits. A loose analogy might be a collection of insects, where you label the insects: that makes the collection contain labels, but it's not a collection of labels. That is, the labels aren't the purpose of the collection.
The actual repository proper is stored in the .git directory (or folder, if you prefer that term). The existence of this folder, with specific files and sub-folders that Git will need, is what tells Git that this is a repository. If the .git directory itself is absent, or is missing these crucial files and/or sub-folders, Git will say that this is not a Git repository.
A bare repository starts and ends with the .git folder (which is then often named repo.git or some such, rather than just .git). Technically a bare repository still has an index / staging-area, but that's just because Git's implementation of the index / staging-area is mainly a file in the .git directory, named index. (This file does not have to exist: Git will create it if needed.)
A non-bare repository, which is the kind you would normally work with, also has a working tree. The working tree is where you see and work with your files. These files are ordinary, everyday files on your computer, stored in folders the way your computer likes to store files. It's important to understand that these working tree files are not in the Git repository.
The repository—the .git folder—stores commits and other internal Git objects. These take the form of an object database, in which Git looks up objects by their hash IDs: large, random-looking numbers, expressed in hexadecimal. All of these objects are in fact read-only: once stored in the database, no object can ever be changed. This means no commit or file can ever be changed.
The repository also stores a separate database of names: branch names, tag names, remote-tracking names, and other names. Each of these stored names holds exactly one hash ID. For branch names, the stored hash ID is always that of a commit. (Tag names are allowed to store other internal-Git-object hash IDs, that you don't normally interact with as directly.) This serves as a good (and fast) way to find particular commits.
Finding, extracting, using, and creating new commits is much of what we do with a Git repository. Since the repository is so commit-centric—and what we do with the repository is definitely commit-centric—it's important to know what a commit is and does, and how Git extracts one:
Each commit stores a full snapshot of all of your files. The files inside the commits are not in the ordinary computer file form.1 Because they are stored as Git objects, they're all read-only. Not even Git can change them. And, because the commit itself is a Git object, it has one of those big ugly hash IDs, so that Git can look it up in the database of objects.
Each commit also stores some metadata: information about the commit itself, such as who made it and when. In this metadata, Git stores the hash ID of a previous commit. More precisely, most commits store the hash ID of one previous commit, but there are some exceptions to this rule.
Because each commit is read-only, but you normally need to read and write your files, you'll tell Git to extract some commit. When you do that, Git will copy the files out of the commit. To do so, Git will read out the internal-only, read-only data from the internally stored files (with their internally-stored names) and turn that into the ordinary file-in-folder setup that your computer uses. Those files will go into your working tree.
In other words, when you run git checkout or git switch, Git performs an optimized (and safety-checked) variant of the following two-step sequence:
First, Git removes all the (tracked) files from your working tree.
Then, Git replaces all the files in your working tree with those from the commit you just checked out.
This is why each commit stores every file: because switching from one commit to another will remove all the files from the commit you're leaving behind, and extract, instead, all the files from the commit you're moving to. The commits themselves are completely read-only, so no commit changes in this process, and no files are lost. Only your working tree is emptied and re-filled.
Note that this talks about switching from one commit to another. Switching from one branch to another doesn't necessarily switch commits. To understand this correctly, we should draw a picture of the commits.
1They are stored in a special, read-only, Git-only form, where they are compressed and—important for Git's internal operations—de-duplicated. This de-duplication step allows every commit to store every file, without taking any extra space. In fact, many different files—plus other internal Git objects—may be stored inside a single computer file (named with some big ugly hash ID followed by .pack), although sometimes file contents are stored as what Git calls loose objects. Either way, though, these files do not have ordinary file names, either.
Drawing pictures of commits in branches
Each commit has some big, ugly, random-looking hash ID. Rather than using these, let's use single uppercase letters to stand in for the hash IDs. And, most commits contain the hash ID of some previous commit. Rather than using that, let's draw an arrow, pointing from the later commit to the previous commit. We'll start with some commit whose hash ID is H:
<-H
The commit H points backwards to, we'll call commit G:
<-G <-H
G of course points to another, still-earlier commit, so we have to keep going:
... <-F <-G <-H
Eventually we'll have a chain of commits that points all the way back to the very first commit ever. Let's call that A here, and draw in all the commits—but I'll get a bit lazy now and use lines to connect them, even though the arrows really only point backwards. Git can't go in and adjust the earlier commit to point forwards, because the earlier commit is frozen for all time, once it's made. So we have:
A--B--C--D--E--F--G--H
Here, commit H is the last commit in the chain. From H we—or Git—can work our way all the way back to the first commit in the chain, commit A; there, everything stops, because there are no earlier commits.
To find commit H quickly, we give it a branch name, like main or master.2 To represent this, let's put in the name, with an arrow coming out of it, pointing to commit H:
...--G--H <-- main
If you now create a new name, such as M01, that name selects the same commit, by default. We now have two names for commit H:
...--G--H <-- main, M01
Your own Git software, working in your own repository—"your Git" for short—can only be on one of these two branches. To represent which one you're on, let's attach the special name HEAD to one of these two branch names:
...--G--H <-- main (HEAD), M01
If we now run:
git switch M01
Git will move HEAD over to the name M01:
...--G--H <-- main, M01 (HEAD)
We're still using commit H, because both names select the same commit. The set of files in your working tree will remain exactly the same, because we didn't change commits.
Now suppose we remove a bunch of files—perhaps an entire folder-full of files—from the working tree, and create some new files, and run git add . to add the removing-and-creating and then run git commit. When we make this new commit, Git will save all the files that are in Git's staging area at that time. The git add . updated the staging area to match our working tree.3 This will make a new commit, which will get some random-looking hash ID, but we'll just call it "commit I". Let's draw it in:
...--G--H
\
I
Commit I has, as its parent, earlier commit H. That's because we were using commit H to make commit I; we were, and still are, on branch M01 as git status will say here, and just a moment ago—before we ran git commit—the name M01 pointed to commit H. But now that commit I exists, the git commit command wrote I's hash ID into the current branch name, so what we have is this:
...--G--H <-- main
\
I <-- M01 (HEAD)
Commit I contains the files we told Git it should have; branch name M01 selects commit I; and HEAD tells us we're on branch M01. If we now run:
git switch main
Git will remove the files that go with commit I and extract the files that go with commit H. Our working tree will now match commit H and we will have:
...--G--H <-- main (HEAD)
\
I <-- M01
as our commit picture.
2Your own Git probably defaults to master while GitHub now default to main, which creates some issues later.
3I've skipped over some important details about .gitignore files, tracked files, untracked files, and how the index works here, so as to concentrate on the commits-and-branches.
What you've gotten wrong
So if I create a new local folder M02 and a branch by typing git branch M02, switching to it by git switch M02, It shows me all of the contents that I have previously added into the M01 branch in the M02 branch ...
The new branch and the old branch currently share the same final commit.
What you see when you look at your working tree are just your working tree files. They're not the same as the committed files, although if git status says that everything matches, they do match the committed files. The committed files do not—cannot—change, ever.
Note that Git stores only files. The files in the commit at the end of your M01 branch may have names like M01/somefile. That's the file's actual name: M01/somefile. It's not a folder named M01 containing a file named somefile. Your working tree has this setup—folders with files in them—and in your working tree, the slashes might even go the other way, M01\somefile. In the commit, though, it's just a file named M01/somefile. This particular point is usually not important, but it means Git is literally incapable of storing an empty folder (because an empty folder contains no files, and Git can only store files).4
but If a remove the files from M02 by typing git rm . -r (it deletes local files), it also deletes the files from both M01 branch and M02 branch.
The git rm -r . operation clears out Git's index / staging-area and your working tree. The existing commits do not—and cannot—change. A future commit that you make right now will contain no files (because you removed all the files); as you put some files back and git add them, the future commit will contain those files.
Git's index / staging-area thus acts as your proposed future commit. You create a file in your working tree and run git add to copy it into Git's staging area. You git checkout or git switch to some existing commit to tell Git: remove all the current committed files and swap in the files from the other commit. This is completely safe if you've committed everything; it's not safe if you have uncommitted work, and git checkout or git switch will normally detect these not-safe cases and avoid clobbering the uncommitted work.5
4There are some tricks with Git submodules that can be used here, but let's not get into those.
5Note that git checkout has a "dangerous" mode of operation that will clobber unsaved work. The new git switch command implements the safe part of git checkout, while the new git restore command implements the unsafe part of git checkout. So it's probably better to learn the new git switch and git restore, so that you always know if you're running a safety-checking command.
For various reasons, both "safe" commands will sometimes let you switch branches even with unsaved work. When they will let you switch, but you forgot to save (add-and/or-commit) first, you can just switch back. This gets quite complicated when one goes into full detail. See Checkout another branch when there are uncommitted changes on the current branch if you really want to know.
Some further detail
ElpieKay's answer shows you how to create more than one root commit. A root commit is a commit like our commit A above: the very first one in a new, empty repository, that has no parent commit. Obviously that first commit can't have a previous commit, so Git has to be able to create a root commit. Using git checkout --orphan or git switch --orphan is another way to create a root commit.
There is a small but critical difference between checkout and switch here:
git checkout --orphan newbranch
leaves Git's index / staging-area full of files (and does not touch your working tree at all), but:
git switch --orphan newbranch
empties out Git's index / staging-area (and as a result, removes any corresponding tracked files from your working tree). If you intend to remove all tracked files, using git switch --orphan saves you this step.
In your case, a good solution is to create an empty root commit from the very beginning. And whenever you want to start a new branch from scratch, just create it from the root commit.
# create the 1st empty root commit
git init
git commit --allow-empty -m"root of all"
# tag it so that you don't have to use its sha1 value which is hard to memorize
git tag root
# create a new branch that tracks no files
git branch newbranch root
It's not late if you create an empty root commit now. Since your repository is not empty, we need other methods to create a root commit.
# create an orphan branch foo from the current branch
git checkout --orphan foo
# remove all cached files and directories
git rm --cached -rf .
# create the empty root commit
git commit --allow-empty -m "root of all"
# tag it
git tag root
# remove the orphan branch foo
git branch -D foo
And here's a 3rd method to create an empty root commit and tag it,
git tag root $(git commit-tree -m"root of all" $(git mktree < /dev/null))

git: how do "fix" a git branch after a soft reset

I always like to review changes on a branch inside my Visual Code Editor. Therefore, I am doing a soft-reset within my terminal:
kk#macbookpro [10457-fix-encryption]$ git rebase origin/master
kk#macbookpro [10457-fix-encryption]$ git reset --soft origin/master
This is really helpful, as I can see the changes which differ from the master directly in the Editor.
Now the problem is, how do I undo the soft-reset? What I was doing so far is, before merging the branch, I have to remove the local branch, and check out the remote branch again:
kk#macbookpro [10457-fix-encryption]$ git checkout master
kk#macbookpro [master]$ git branch -D 10457-fix-encryption
kk#macbookpro [master]$ git checkout 10457-fix-encryption
kk#macbookpro [10457-fix-encryption]$ git rebase origin/master
kk#macbookpro [10457-fix-encryption]$ git log
// check if everything is ok
kk#macbookpro [10457-fix-encryption]$ git checkout master
kk#macbookpro [master]$ git merge --no-ff 10457-fix-encryption
But as you can see, this is very complicated. And I am sure, this is not the "git way". How to achieve this with less git commands?
Please help!
To fix an erroneous or unwanted soft reset, simply use another soft reset. The trick here is finding the right hash ID. Here, reflogs will help: the reflog for the branch that was soft-reset will have in it the hash ID that you would like to use to restore it. In this particular case, you could also use ORIG_HEAD, so that after your:
git reset --soft origin/master
you could:
git reset --soft ORIG_HEAD
and be back where you were.
I suspect there's no reason to do that soft-reset in the first place. There might be, depending on just how anti-clever this "Visual Code Editor" thing may be. But it seems unlikely.
The thing to keep in mind here is that branch names are just mostly just fancy—or convenient—ways of specifying one particular commit. There are plenty of other ways to specify some particular commit. Let's take a look at your terminal commands here:
kk#macbookpro [10457-fix-encryption]$ git rebase origin/master
This takes some set of commits and copies them to new and improved commits. It then takes the current branch name—in this case, 10457-fix-encryption—and makes it point to the last such copied commit.
The tricky part here is determining exactly which commits will get copied. The commits that are copied are a subset of those listed by:
git log origin/master..HEAD
before you run the git rebase command. That is, Git will find those commits that are reachable from HEAD, but not reachable from origin/master.
The "subset of" above covers a bunch of special cases, where git rebase discards commits that do not need to be copied, or shouldn't be copied, for some particular reason. If we ignore these special cases—which we probably should here—we can draw a typical rebase situation like this:
L <-- origin/master
/
...--G--H <-- master
\
I--J--K <-- 10457-fix-encryption (HEAD), origin/10457-fix-encryption
Here, each commit hash ID has been replaced with an uppercase letter. Commits in Git form backwards-looking chains: commit H, being the last commit on master, points backwards to an earlier commit G, which points backwards further, and so on. Meanwhile commit K is the last commit on 10457-fix-encryption: it points backwards to commit J, which points to commit I, which points to commit H. You've already run a git push origin so that commits I-J-K are in another Git repository. They're mostly ready to be incorporated as is right now, except for one problem: someone else has updated master over on origin by adding a new commit (or perhaps several, but we'll just draw the one here). So commit L is the tip of their branch.
The rebase operation will list out commit hash IDs for commits I, J, and K, as those are the commits that are on 10457-fix-encryption (which consists of all commits on master plus I-J-K), minus all commits on origin/master (which consists of commits up through H plus commit L). The set-subtraction operation here leaves exactly those three commits that are only on 10457-fix-encryption. Other set-subtraction operations would work too, but this one is fine.
Next, the rebase operation will pick commit L as the place the copies should go. That's because origin/master picks commit L. Git will use its so-called detached HEAD mode to check out commit L:
L <-- HEAD, origin/master
/
...--G--H <-- master
\
I--J--K <-- 10457-fix-encryption, origin/10457-fix-encryption
The rebase operation will now proceed to copy, as if by using git cherry-pick, each of the commits it listed out earlier. If and when each copy succeeds, this makes a new commit that applies the same change as the original commit. We can draw these copies like this:
I'-J'-K' <-- HEAD
/
L <-- origin/master
/
...--G--H <-- master
\
I--J--K <-- 10457-fix-encryption, origin/10457-fix-encryption
Note that at the end of this particular stage of the rebase, HEAD now selects copy K', which corresponds to commit K. The rebase code now finishes the rebase by plucking the name 10457-fix-encryption away from commit K and making it point instead to commit K':
I'-J'-K' <-- 10457-fix-encryption (HEAD)
/
L <-- origin/master
/
...--G--H <-- master
\
I--J--K <-- origin/10457-fix-encryption
kk#macbookpro [10457-fix-encryption]$ git reset --soft origin/master
What this does is to move the current branch name, without changing what's in Git's index, and without changing what's in your working tree. So now the picture of the commit graph looks like this:
I'-J'-K' ???
/
L <-- 10457-fix-encryption (HEAD), origin/master
/
...--G--H <-- master
\
I--J--K <-- origin/10457-fix-encryption
Note that there's no branch name for commit K' any more. There are several ways to find it: ORIG_HEAD, 10457-fix-encryption#{1}, and HEAD#{1} will all produce the hash ID of commit K':
git rev-parse ORIG_HEAD
for instance will print out the desired hash ID.
(Hash IDs have the advantage that they never change. They have the disadvantage of being very long and hard to type and impossible to remember, which is most of why we mostly use names instead of hash IDs. Names have the further advantage that we can—as git rebase or git reset do—move them to make them select some other commit. But this of course is also a disadvantage of names: when we move them, we forget what hash ID they used to specify. That's what reflogs are for, but the entries in reflogs all look alike—except for the hash IDs of course—and can be tricky to use.)
Apparently, by having the current branch name identify commit L while Git's index and your working tree have, as their contents, the contents of files as they appear in commit K', your editor lets you see these as changes. But git diff will let you see these as changes, without all the mucking about with git reset. Just run:
git diff origin/master HEAD
to compare those two commits. (Personally, I like to look at git log -p output, or git format-patch output, so that I can see each commit as a diff against its predecessor.) Or find some way to make your editor look at some specific commit as compared to the current files, rather than using the current branch name. (But maybe there's no way to do that, depending on your editor.)
So, if you really do need to do this, you can do it without moving the name 10457-fix-encryption at all. Just make some other temporary name:
git checkout -b tmp
so that you have:
I'-J'-K' <-- 10457-fix-encryption, tmp (HEAD)
/
L <-- origin/master
/
...--G--H <-- master
\
I--J--K <-- origin/10457-fix-encryption
You can now git reset --soft origin/master to get:
I'-J'-K' <-- 10457-fix-encryption
/
L <-- origin/master, tmp (HEAD)
/
...--G--H <-- master
\
I--J--K <-- origin/10457-fix-encryption
When you're done comparing, do another reset (soft, hard, or mixed: it doesn't matter here as what's in the commit you're resetting to is what's in Git's index and your work-tree) to the updated branch, after which Git will let you delete tmp, then check out master:
git reset 10457-fix-encryption
git checkout 10457-fix-encryption
git branch -d tmp
git checkout master
Or once you're a bit more confident with this workflow, you can:
git reset --hard && git checkout master && git branch -D tmp
which shuts off various safety checks but gets you down to three commands.

Git: How to update the master branch and switch to it in one step?

I'm working on a project that's hosted on GitLab and uses issue/work branches and merge requests to bring that work into the master branch when it's done. Usually I work on issue branches. When it has been merged by GitLab, I need to switch to the current master to do a build, locally.
My workflow is this:
Switch to master
Pull from remote (--ff-only)
Remove stale remote tracking branches
Also remove their local tracking branches
There's also a client-side tool that watches the code directory and updates some files (CSS, JavaScript). When it sees a change in the first step (switch to master), I first need to wait for it to finish before going on (to avoid confusion). If there's a difference between the issue branch and the old master, there's a good chance that the difference will disappear when updating master (as that issue branch is now merged).
I'm looking for a way to switch to the already-updated master branch in one step. How can I do that with a git command? I want to bundle up all these actions in a batch file to avoid repeating all those manual steps in TortoiseGit every time.
This question is different from the suggested one in that the local master branch already exists. I'm not switching to a new branch from a remote, but to a branch that already exists and it just behind the remote.
TL;DR
Unless you write your own script (or use a Git alias to run multiple commands and/or scripts), you can't get this down to a single command, but you can get closer. See the long section for many caveats: the biggest one is that it assumes you're not already on master when you do it. If you are, the second step won't work (see the long section for what will).
git fetch -p &&
git fetch . refs/remotes/origin/master:refs/heads/master &&
git checkout master
will take care of the first three bullet points—not in the same order—with a single work-tree-updating git checkout step.
(Note that I split this into three lines for posting purposes, but as a Git alias using !, it's really all one big line.)
Long
There are several approaches, including actual, literal batch files (shell scripts on Unix-like systems, or .BAT files, or whatever) and aliases (as suggested by Joe in a comment).
There's also a client-side tool that watches the code directory and updates some files ...
This is ... not necessarily a good idea, let's say. :-)
While git checkout master runs, it's changing various files. Let's say that for some reason, it changes one of several files that the watcher watches, but then it pauses for a few minutes (or seconds, or microseconds, or some unit of time anyway). While it is paused, the watcher tries to combine the multiple files that are now out of sync.
Maybe this is OK and self-correcting when Git un-pauses and finishes the checkout—but it might be better if you could make sure the update only happens when the checkout is done.
That aside, let's take a look at this particular series of commands, and be very concrete about which Git command you're using:
Switch to master
I assume this is git checkout master.
Pull from remote (--ff-only)
I assume this is git pull origin master --ff-only or perhaps just git pull --ff-only.
Remove stale remote tracking branches
I'll assume for now that this is git fetch --prune. If you are doing something different, you should include that in your question.
Also remove their local tracking branches
If I understand what you mean, this requires a script. Note that this is somewhat dangerous: suppose you have your own branch X on which you are doing development. This X is not related to anyone else's X. Then someone creates their own X—using the same name—and sends it to the machine from which you git fetch. You now have origin/X. Then they delete their X (because they're done with it) and delete origin/X. If you now have your script delete your X, because origin/X went away, that would probably be bad.
If you only delete your X when it explicitly has origin/X set as its upstream, this particular case won't occur—but if someone accidentally deletes your origin/X thinking it was their origin/X, the same problem crops up again, and this time that particular protection does not work.
Anyway, with all that aside, let's look at the variant I suggested above.
git fetch -p
This updates all your origin/* names,1 including origin/master, without affecting any files in your working tree. The -p is short for --prune, so it deletes any origin/* names that no longer have a corresponding branch in the Git over at the URL stored under the name origin.
1I assume here that you have only one remote, which is named origin. If you have more than one remote, use git fetch origin -p to make sure you're fetching specifically from the one named origin. I also assume you have not configured your Git to be a single-branch clone.
git fetch . refs/remotes/origin/master:refs/heads/master
This rather magic-looking command tells your Git to call itself up. That is, the special name . refers to your own Git repository. We are using this to trick your Git into fast-forwarding your master branch based on your updated origin/master. The final argument is what does this: we say to your Git: OK, my Git, when you talk to that other Git, find out what commit its refs/remotes/origin/master identifies. Then, if that's a fast-forward operation, update my refs/heads/master to match.
Of course, the "other Git" your Git is talking to is itself—so this means fast-forward my master from my origin/master.2 It's roughly equivalent to:
git checkout master && git merge --ff-only origin/master && git checkout -
except that no actual checking-out occurs: no files in your work-tree change.
2You might wonder why some of these use origin/master and some use refs/remotes/origin/master. The longer one is just the full spelling of the name. When using git fetch, it's wise to use the full spellings. In fact, in general, in scripts, you might want to use full spellings more often, but specifically git fetch can become confused if the other Git you talk to accidentally has both a branch and a tag with the same name, for instance. So I'm illustrating the full names with git fetch. You'll use it to talk to your own Git, so if you don't mix up your tags and branch names or otherwise create ambiguity, you won't actually need the full names. But it's a good habit with git fetch.
The above fails if you're on your master
The git fetch command will refuse to fetch into whatever branch name you have checked out. So if you are on master, this git fetch . trick will fail.
In a way, this is OK! If you are on your master, what you should do instead is run:
git merge --ff-only origin/master
or anything equivalent. This is what your git pull --ff-only does: first it runs git fetch (without the -p and limited to fetching only the other Git's master); then it runs git merge --ff-only.
A more complete version
A more complete version of this sequence, then, is to first check: Which branch am I on? To do that, you can use either of two Git commands:
git rev-parse --abbrev-ref HEAD
or:
git symbolic-ref --short HEAD
Both of these will print master if you are currently on your own master branch. The difference between them is what they do if you're on no-branch-at-all: e.g., in the middle of a rebase, when you are in "detached HEAD" state. In that case, the second command—the git symbolic-ref one—errors out, while the first one just prints HEAD.
If you'd like to avoid doing any of this when in such a state, use the second command and check for failure. Otherwise, use the first one. I'll illustrate just the first one here:
if test $(git rev-parse --abbrev-rev HEAD) = master; then
# already on master - use alternative strategy
git fetch -p && git merge --ff-only refs/remotes/origin/master
else
# not currently on master: use fancy tricks to update
git fetch -p &&
git fetch . refs/remotes/origin/master:refs/heads/master &&
git checkout master
fi
The above, while untested, should be suitable as a shell script. If you have Git installed, you have the ability to run shell scripts—or you can turn the above into a very long Git alias, using ! and the appropriate set of semicolons.

How to merge a stash after removing a file locally and commiting the change?

Currently had issues with 'UserInterfaceState.xcuserstate' while working with another developer and using Git as our source control. I realized early on that .gitignore wasn't ignoring that file and I decided to remove/untrack it with 'git rm --cached'. Before executing the command, I stashed my current changes to:
Keep my current changes
Create a quick commit that reflects the removal of UserInterfaceState.xcuserstate
I created the commit the reflected the removal of 'UserInterfaceState.xcuserstate' and proceeded with 'git stash apply'. This was the error that I received:
needs merge
unable to refresh index
I'm aware that the stash still has UserInterfaceState.xcuserstate but I was under the impression that Git would merge the newly committed state of the master branch with the stash and force me to resolve the merge conflict.
How would I be able to merge the stash to regain my previous work to the newly committed master branch the no longer has nor tracks UserInterfaceState.xcuserstate?
Whenever you have a stash that won't apply, I recommend turning that stash into a branch.
To do that, first make sure you have no uncommitted work. This means you should now commit whatever you're doing, or stash it—the latter is a bit ironic and weird but works fine, as long as you remember that the stash you're converting has now been renumbered. Now that everything is "clean", pick the troublesome stash by its ID—stash#{1} or stash#{6} or whatever if necessary, or just the default if it's the current or only stash—and use that ID (or the default) as an argument to the git stash branch command:
$ git stash branch newbranch stash#{3}
You're now in a state where you can run git status, then maybe git commit, or git add and git commit, etc—your stash#{3} stash has been re-loaded into the index, as if you had run git stash apply --index, and into your work-tree as if by the same command. Once you make the commit(s), your new branch is ready to be used however you like.
(In fact, Git really did run the command: first, Git ran git checkout of the appropriate commit. Then Git ran git stash apply --index on the stash, which restored the saved index and the saved work-tree. Then, having succeeded at the applying, Git ran git stash drop on that saved stash.)
In this case, after git stash branch name stash, you'll probably want one git add and git commit, which will save all of your work including the pesky not-removed, maybe-modified-maybe-not UserInterfaceState.xcuserstate file. But now you can git checkout the branch you were working on, apply and drop the temporary stash you made if you made one, and git cherry-pick -n the commit on that new branch you made from your troublesome stash. If that commit has no changes to the pesky file, and the pesky file doesn't exist here, the cherry-pick is likely to go pretty smoothly and you'll be all set.
Discussion
This all makes a certain amount of sense once you realize that (a) a git stash object is just a clump of commits; (b) commits always record their parent commits; and (c) a branch is just a series of commits designated by a branch name identifying the last commit that is to be considered part of the branch.
If the way git stash made its little clump-of-commits were normal, you'd have:
...--o--o--*--o <-- your-branch
\
i--w <-- stash
and hence the stash would really be just like any branch, except for the fact that refs/stash does not start with refs/heads/ (compare with refs/heads/your-branch, which obviously does start with refs/heads/). Here, * is the commit that you were on when you ran git stash. You added one more commit, which is the one just to the right of the *. The i and w commits here would contain the index and work-tree states as of when you ran git stash.
The tricky part is that git stash doesn't make i and w like this at all. Instead, it makes:
...--o--o--*--o <-- your-branch
|\
i-w <-- stash
That is, the saved work-tree state appears to be a merge commit, saving the result of merging your index commit i and the commit you were on when you ran git stash (commit *). It isn't really a merge, in that it's not what you'd get by running git merge: the stash code is just abusing the merge-commit format, to make it easier for the stash code later.
Fortunately, git stash branch knows just how to de-abuse the format. It:
checks out commit *;
makes a new branch name to remember where you are now; and
applies the stash, keeping its index and work-tree components separate (just as they were when you saved the stash) in case that's what you intended.
So now you have:
o <-- your-branch
/
...--o--o--* <-- new-branch
with i in your index (ready to be committed) and w back in your work-tree (ready to be git add-ed to update your index, after which w will be in your index and ready to be committed). Commit *, of course, has the pesky file in it, so leave the pesky file where it is with no changes, add and commit everything else, and you get:
o <-- your-branch
/
...--o--o--*--W <-- new-branch
where W is a regular ordinary commit of the work-tree state you had, ready to be given to git cherry-pick (probably with -n) or used however you like.
Eventually, you can simply delete the branch new-branch (which you probably want to call by some other name) and commit W will be abandoned and will eventually disappear entirely.

Resources