I set up a new Git repository using the following commands:
mkdir plans-for-world-domination
cd plans-for-world-domination
git init
echo "MWA HA HA HA HA!" > plans.txt
git add .
git commit -m "Beginning my plans..."
Then I made a clone of this repository, made some changes, committed them, and then tried to push:
cd ..
git clone plans-for-world-domination clone
cd clone
echo "Step 1: set up super secret spy base in Cleveland, Ohio" >> plans.txt
git commit -am "Update plans"
git push origin master
When I cd
back into the plans-for-world-domination
repository though, there are changes staged in the staging-area/index that are the reverse of the changes that I just pushed:
$ cd ../plans-for-world-domination
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: plans.txt
$ git diff --staged
diff --git a/plans.txt b/plans.txt
index febb495..ce01362 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,2 +1 @@
MWA HA HA HA HA!
-Step 1: set up super secret spy base in Cleveland, Ohio
Why does my first repo have these unstaged changes that are the reverse of what I just pushed, and how can I fix this?
The staging area in the first repository appears to contain the reverse of the changes that were just pushed because it's a non-bare repository, meaning that it contains a working copy, which is also frequently referred to as a working (directory) tree in the Git documentation. Bare repositories, on the other hand, don't have a working copy directory.
Since the repository is non-bare, when you push to it, the push only updates branch references, and the symbolic HEAD
reference, because git push
doesn't operate on the working-copy and staging area that are present in non-bare repos.
As a consequence of this, the working-copy and staging-area of the non-bare repo are still left on the same state of the repository that was present before the push that updated HEAD
. In other words, the actual state of the working-copy and staging-area do not match the state of the commit pointed to by HEAD
. This is why those differences between the two states show up when git status
and git diff
are run:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: plans.txt
$ git diff --staged
diff --git a/plans.txt b/plans.txt
index febb495..ce01362 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,2 +1 @@
MWA HA HA HA HA!
-Step 1: set up super secret spy base in Cleveland, Ohio
Since the working-copy and staging-area are out of sync with HEAD
, a solution to get them to match again is to simply use
git reset --hard HEAD
git reset --hard
to reset the working-coy and staging-area to the commit pointed to by HEAD
.
However, this is not the ideal solution...
You're not really supposed to push to non-bare repositories, because of this exact issue of their working-copies and staging-areas de-syncing with the repository references. Instead, unless you have an unusual reason to push to a non-bare repository, you really ought to push to bare repositories instead, which don't have a working copy.
To create a bare repository, simply use the --bare
flag:
# Initialize a bare repo
mkdir bare
cd bare
git init --bare
# Push changes to the bare repo
cd ..
mkdir project
cd project
# Make some changes and commit
git remote add origin ../bare
git push origin master
# Or create a bare clone from another bare or non-bare repo
git clone --bare <repo-path-or-uri>
Note that since Git version 1.6.2, pushing to non-bare repositories has been denied by default:
With the next major release,
git push
into a branch that is currently checked out will be refused by default. You can choose what should happen upon such a push by setting the configuration variablereceive.denyCurrentBranch
in the receiving repository.
In fact, when you try to push to a non-bare repo with current versions of Git, you're push should be denied with the following error message (slightly modified for brevity):
$ git push origin master
Total 0 (delta 0), reused 0 (delta 0)
error: refusing to update checked out branch: refs/heads/master
error: By default, updating the current branch in a non-bare repository
error: is denied, because it will make the index and work tree inconsistent
error: with what you pushed, and will require 'git reset --hard' to match
error: the work tree to HEAD.
error:
error: You can set 'receive.denyCurrentBranch' configuration variable to
error: 'ignore' or 'warn' in the remote repository to allow pushing into
error: its current branch; however, this is not recommended unless you
error: arranged to update its work tree to match what you pushed in some
error: other way.
error:
error: To squelch this message and still keep the default behaviour, set
error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To non-bare
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to 'non-bare'
As the error message above explains, you can disable the safety checks that prevent you from pushing into a non-bare repo by disabling the receive.denyCurrentBranch
config setting in the remote non-bare repo:
git config receive.denyCurrentBranch warn # Warn when pushing to non-bare repo
git config receive.denyCurrentBranch ignore # Don't even bother warning