为什么我的远程Git存储库在推送后会有未提交的更改?

2 git git-push git-non-bare-repository

我使用以下命令设置了一个新的Git存储库:

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..."
Run Code Online (Sandbox Code Playgroud)

然后我复制了这个存储库,进行了一些更改,提交了它们,然后尝试推送:

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
Run Code Online (Sandbox Code Playgroud)

当我cd回到plans-for-world-domination存储库时,staging-area/index中有一些更改与我刚刚推送的更改相反:

$ 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
Run Code Online (Sandbox Code Playgroud)

为什么我的第一个repo具有与我刚刚推送的相反的非分段更改,以及如何解决这个问题?

小智 7

推送不会更新非裸存储库中的工作副本和临时区域

第一个存储库中的暂存区域似乎包含刚刚推送的更改的反向,因为它是一个非裸存储库,这意味着它包含一个工作副本,它通常也被称为工作(目录)树. Git文档.另一方面,存储库没有工作副本目录.

由于存储库是非裸的,因此当您按下它时,push仅更新分支引用和符号HEAD引用,因为git push它不对非裸存储库中存在的工作副本和暂存区域进行操作.

因此,非裸仓库的工作副本和暂存区域仍保留在更新推送之前存在的存储库的相同状态HEAD.换句话说,工作副本和临时区域的实际状态与指向的提交状态不匹配 HEAD.这就是为什么两个州之间的差异出现git statusgit diff运行的原因:

$ 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
Run Code Online (Sandbox Code Playgroud)

(次优)解决方案:硬重置

由于工作副本和临时区域不同步HEAD,使它们再次匹配的解决方案就是简单地使用

git reset --hard HEAD
git reset --hard
Run Code Online (Sandbox Code Playgroud)

将working-coy和staging-area重置为指向的提交HEAD.

但是,这不是理想的解决方案......

理想的解决方案:推送到裸存储库

您并不是真的应该推送到非裸存储库,因为它们的工作副本和暂存区域的确切问题与存储库引用不同步.相反,除非你有一个不寻常的理由推送到非裸存储库,否则你真的应该推送到裸存储库而不是工作副本.

要创建裸存储库,只需使用--bare标志:

# 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>
Run Code Online (Sandbox Code Playgroud)

自Git 1.6.2起,默认情况下拒绝推送到非裸存储库

请注意,自Git 1.6.2版以来,默认情况下拒绝推送到非裸存储库:

在下一个主要版本中,git push默认情况下将拒绝进入当前签出的分支.您可以通过receive.denyCurrentBranch在接收存储库中设置配置变量来选择在此类推送时应该发生的事情.

实际上,当您尝试使用当前版本的Git推送到非裸仓库时,应该拒绝推送以下错误消息(为简洁起见略微修改):

$ 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'
Run Code Online (Sandbox Code Playgroud)

正如上面的错误消息所解释的那样,您可以通过禁用远程非裸存储库中的receive.denyCurrentBranch配置设置来禁用安全检查,以防止您进入非裸存储库:

git config receive.denyCurrentBranch warn   # Warn when pushing to non-bare repo
git config receive.denyCurrentBranch ignore # Don't even bother warning
Run Code Online (Sandbox Code Playgroud)