昨天,我发布了一个关于如何从我的一台机器克隆Git存储库到另一台机器的问题,我如何从另一台机器“克隆Git”?

现在我可以成功地将Git存储库从源存储库(192.168.1.2)克隆到目标存储库(192.168.1.1)。

但是当我对文件进行编辑,git commit -a -m "test"和git push时,我在目的地(192.168.1.1)上得到了这个错误:

git push                                                
hap@192.168.1.2's password: 
Counting objects: 21, done.
Compressing objects: 100% (11/11), done.
Writing objects: 100% (11/11), 1010 bytes, done.
Total 11 (delta 9), 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 git+ssh://hap@192.168.1.2/media/LINUXDATA/working
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to 'git+ssh://hap@192.168.1.2/media/LINUXDATA/working'

我正在使用两个不同版本的Git(远程上的1.7版本和本地机器上的1.5版本)。这是一个可能的原因吗?


当前回答

检查目标项目中的.git/config:

$ cat .git/config 
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[receive]
    denyCurrentBranch = updateInstead

如果核心。Bare为false,你可以设置为true:

$ git config core.bare true

然后在本地推送到远程:

git push remote_repo   // suppose the destination repo is remote_repo

它会成功,在remote_repo你可以检查git版本。

$ git log -1
commit 0623b1b900ef7331b9184722a5381bbdd2d935ba
Author: aircraft < aircraft_xxx@126.com>
Date:   Thu May 17 21:54:37 2018 +0800

现在你不能在你的“工作区”中使用git了:

$ git status
fatal: This operation must be run in a work tree

你应该裸露。裸背为假。

$ git config core.bare false

其他回答

我不得不在一个现有的裸库中重新运行git——init,这已经在裸库树中创建了一个.git目录-我意识到在那里输入git状态后。我把它删除了,一切又恢复正常了:)

(所有这些答案都很棒,但就我而言,我的情况完全不同(据我所知)。)

使用Git,两个常规(非裸)存储库不能直接来回推/拉文件。必须有一个中间裸存储库。显然,这有点像一对已婚夫妇有了一个孩子,但这对夫妇要离婚了。父母之间不会相互交谈,但他们会通过孩子进行交流。

因此,您有一个存储库,您将这个存储库克隆到一个裸存储库,然后将其克隆到第三个。第一个和第三个可以通过第二个存储库(裸存储库)交换信息。我想这是有道理的,因为你不希望别人在未经你同意的情况下将东西签入你的存储库,因为这可能会导致合并冲突等。

这里有一个例子:

在PC上,在~/workspace

git init
echo "line 1" > afile.txt
git add .
git commit -m ‘initial import’
git clone --bare . ../remote-repository.git
git remote add origin ../remote-repository.git
git push --set-upstream origin master

在笔记本电脑上,在~/workspace中(不要init git等)

git clone //LJZ-DELLPC/remote-repository.git/ .

//然后进行各种提交,并推送它们:

echo "line 2" > afile.txt
git add afile.txt
git commit -m 'added line 2'
git push    

然后回到PC,在~/workspace中

git pull

//然后进行各种提交,并推送它们:

git push

在笔记本电脑 git拉

等等。

下面是一台机器上的一个绝对具体的例子,直接从命令窗口复制,这样我们就知道没有遗漏任何步骤,它确实可以工作,等等:

lylez@LJZ-DELLPC ~
$ cd gitdir
/home/lylez/gitdir

lylez@LJZ-DELLPC ~/gitdir
$ ls

lylez@LJZ-DELLPC ~/gitdir
$ mkdir repo1

lylez@LJZ-DELLPC ~/gitdir
$ cd repo1
/home/lylez/gitdir/repo1

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git init
Initialized empty Git repository in /home/lylez/gitdir/repo1/.git/

lylez@LJZ-DELLPC ~/gitdir/repo1
$ echo "line 1" > afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git add afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git commit -m 'initial import'
[master (root-commit) f407e12] initial import
 1 file changed, 1 insertion(+)
 create mode 100644 afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git clone --bar . ../repo1-bare-clone
Cloning into bare repository '../repo1-bare-clone'...
done.

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git remote add origin ../repo1-bare-clone

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git push --set-upstream origin master
Branch master set up to track remote branch master from origin.
Everything up-to-date

lylez@LJZ-DELLPC ~/gitdir/repo1
$ cd ..

lylez@LJZ-DELLPC ~/gitdir
$ ls
repo1  repo1-bare-clone

lylez@LJZ-DELLPC ~/gitdir
$ mkdir repo1-remote

lylez@LJZ-DELLPC ~/gitdir
$ cd repo1-remote
/home/lylez/gitdir/repo1-remote

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ git clone ../repo1-bare-clone .
Cloning into '.'...
done.

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ ls
afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ cat afile.txt
line 1

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ echo "line 2" >> afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ git add afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ git commit -m 'added line 2'
[master 5ad31e0] added line 2
 1 file changed, 1 insertion(+)

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ git push
Counting objects: 3, done.
Writing objects: 100% (3/3), 260 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /home/lylez/gitdir/repo1-remote/../repo1-bare-clone
   f407e12..5ad31e0  master -> master

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ cd ../repo1

lylez@LJZ-DELLPC ~/gitdir/repo1
$ ls
afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1
$ cat afile.txt
line 1

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git pull
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From ../repo1-bare-clone
   f407e12..5ad31e0  master     -> origin/master
Updating f407e12..5ad31e0
Fast-forward
 afile.txt | 1 +
 1 file changed, 1 insertion(+)

lylez@LJZ-DELLPC ~/gitdir/repo1
$ cat afile.txt
line 1
line 2

lylez@LJZ-DELLPC ~/gitdir/repo1
$ echo "line 3" >> afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git add afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git commit -m 'added line 3'
[master 3fa569e] added line 3
 1 file changed, 1 insertion(+)

lylez@LJZ-DELLPC ~/gitdir/repo1
$ git push
Counting objects: 3, done.
Writing objects: 100% (3/3), 265 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../repo1-bare-clone
   5ad31e0..3fa569e  master -> master

lylez@LJZ-DELLPC ~/gitdir/repo1
$ cd ../repo1-remote/

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ ls
afile.txt

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ cat afile.txt
line 1
line 2

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ git pull
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/lylez/gitdir/repo1-remote/../repo1-bare-clone
   5ad31e0..3fa569e  master     -> origin/master
Updating 5ad31e0..3fa569e
Fast-forward
 afile.txt | 1 +
 1 file changed, 1 insertion(+)

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ cat afile.txt
line 1
line 2
line 3

lylez@LJZ-DELLPC ~/gitdir/repo1-remote
$ git --version
git version 2.1.1

lylez@LJZ-DELLPC ~/gitdir/repo1-remote

你可能做了什么导致了这个:

当你去开发一个小程序时,这种事情就会发生。你要改变一些已经生效的东西,所以你施放了三级永久撤销法术:

machine1:~/proj1> git init

然后开始添加/提交。但随后,项目开始变得更加复杂,你想从另一台计算机(比如你的家用PC或笔记本电脑)上处理它,所以你做了这样的事情

machine2:~> git clone ssh://machine1/~/proj1

它会复制,一切看起来都很好,所以你可以从machine2上处理代码。

然后……您尝试从machine2推送提交,并在标题中得到警告消息。

The reason for this message is because the git repo you pulled from was kinda intended to be used just for that folder on machine1. You can clone from it just fine, but pushing can cause problems. The "proper" way to be managing the code in two different locations is with a "bare" repo, like has been suggested. A bare repo isn't designed to have any work being done in it, it is meant to coordinate the commits from multiple sources. This is why the top-rated answer suggests deleting all files/folders other than the .git folder after you git config --bool core.bare true.

Clarifying the top-rated answer: Many of the comments to that answer say something like "I didn't delete the non-.git files from the machine1 and I was still able to commit from machine2". That's right. However, those other files are completely "divorced" from the git repo, now. Go try git status in there and you should see something like "fatal: This operation must be run in a work tree". So, the suggestion to delete the files isn't so that the commit from machine2 will work; it's so that you don't get confused and think that git is still tracking those files. But, deleting the files is a problem if you still want to work on the files on machine1, isn't it?

那么,你到底应该怎么做呢?

这取决于你还打算在machine1和machine2上工作多少…

如果你已经完成了从machine1开始的开发,并将所有的开发都转移到machine2上……只需要做排名最高的答案所建议的:git config——bool core。完全正确,然后,可选地删除该文件夹中除.git之外的所有文件/文件夹,因为它们无法跟踪,可能会引起混乱。

如果你在machine2上的工作只是一次性的,你不需要在那里继续开发……那就别费事去做单纯的回购了;只是ftp / rsync / scp /等。你从机器*2*上的文件放到机器*1*上的文件上面,从机器*1*上提交/推送,然后删除机器*2*上的文件。其他人建议创建一个分支,但我认为如果您只是想合并从另一台机器上一次性完成的一些开发,那么这有点混乱。

如果你需要在machine1和machine2上继续开发……然后你需要正确地设置。您需要将您的repo转换为bare,然后您需要在machine1上创建一个副本,以便您在其中工作。可能最快的方法就是去做

machine1:~/proj1> git config --bool core.bare true
machine1:~/proj1> mv .git/ ../proj1.git
machine1:~/proj1> cd ..
machine1:~> rm -rf proj1
machine1:~> git clone proj1.git
machine1:~> cd proj1

非常重要:因为您已经将回购的位置从proj1移动到proj1。Git,您需要在machine2上的. Git /配置文件中更新它。之后,您可以从machine2提交更改。最后,我试着把我的裸回购放在一个中心位置,远离我的工作树(也就是说,不要把“proj1。Git '和'proj1'在同一个父文件夹中)。我建议您也这样做,但我想让上面的步骤尽可能简单。

旧版本的Git允许推入当前签出的非裸库分支。

事实证明,这是一件非常令人困惑的事情。所以他们添加了你看到的警告信息,这也是非常令人困惑的。

如果第一个存储库只是充当服务器,那么按照其他答案的建议将其转换为裸存储库,然后就可以完成了。

然而,如果你需要在两个都在使用的回购之间有一个共享分支,你可以通过下面的设置来实现

Repo1 -将充当服务器,也用于开发

Repo2—仅用于开发

按如下方式安装Repo1

创建一个分支来共享工作。

git branch shared_branch

为了安全起见,您还应该创建一个$(REPO)。Git /hooks/update拒绝对shared_branch以外的任何内容的任何更改,因为你不想让别人乱动你的私有分支。

repo1/.git/hooks  (GIT_DIR!)$ cat update
#!/bin/sh
refname="$1"
oldrev="$2"
newrev="$3"

if [ "${refname}" != "refs/heads/shared_branch" ]
then
   echo "You can only push changes to shared_branch, you cannot push to ${refname}"
   exit 1
fi

现在在repo1中创建一个本地分支,您将在其中执行实际工作。

git checkout -b my_work --track shared_branch
Branch my_work set up to track local branch shared_branch.
Switched to a new branch 'my_work'

(可能需要git配置——global push.default upstream,以便git push工作)

现在您可以使用

git clone path/to/repo1 repo2 
git checkout shared_branch 

此时,您已经设置了repo1和repo2,以便在本地分支上工作,从repo1中的shared_branch中进行推和拉操作,而无需担心错误消息或repo1中的工作目录不同步。您使用的任何正常工作流都应该工作。

总结

您不能推送到存储库的一个签出分支,因为这会扰乱该存储库的用户,很可能以丢失数据和历史记录而告终。但是您可以推送到同一存储库的任何其他分支。

由于裸存储库从来没有检出过任何分支,所以您总是可以推入裸存储库的任何分支。

根据您的需要,有多种解决方案。

解决方案1:使用裸存储库

正如建议的那样,如果在一台机器上不需要工作目录,则可以移动到裸存储库。为了避免混淆存储库,你可以克隆它:

machine1$ cd ..
machine1$ mv repo repo.old
machine1$ git clone --bare repo.old repo

现在你可以把所有你想要的推送到和以前一样的地址。

解决方案2:推到一个非签出分支

但是如果您需要检出remote <remote>上的代码,那么您可以使用一个特殊的分支来进行推送。假设在本地存储库中,您已经调用了远程源,并且处于分支master上。然后你就可以

machine2$ git push origin master:master+machine2

然后你需要合并它当你在原始远程回购:

machine1$ git merge master+machine2

问题的解剖

当一个分支签出时,提交将添加一个新的提交,当前分支的头作为其父,并移动该分支的头作为新提交。

So

A ← B
    ↑
[HEAD,branch1]

就变成了

A ← B ← C
        ↑
    [HEAD,branch1]

但如果有人可以推送到中间的那个分支,用户就会进入git所说的分离头部模式:

A ← B ← X
    ↑   ↑
[HEAD] [branch1]

现在用户不再在branch1中,没有显式地要求签出另一个分支。更糟糕的是,用户现在在任何分支之外,任何新的提交都会悬空:

      [HEAD]
        ↓
        C
      ↙
A ← B ← X
        ↑
       [branch1]

假设,如果此时用户签出了另一个分支,那么这个悬空提交将成为Git垃圾收集器的公平游戏。