多分支间的Git暂存和提交

20

我显然完全不理解git。这就是我得到的:

git branch  (outputs that I'm on master)
git checkout -b foo
echo "next line" >> file (file is an existing file)
git add file (stages)
git checkout master
git status (shows that file has "next line" and is staged!!)
git commit (commits the changes that were staged on branch foo!!)
git checkout foo

问题的关键在于,现在对文件所做的任何更改(包括修改文件和暂存)都会发生在所有分支上。当你提交到特定的分支时,那些更改将丢弃除了你提交到的那一个分支以外的所有其他分支。

这实际上就是正在发生的事情吗?有人能让我理解一下吗?这听起来像是完全混乱的行为,显然我不明白设计理念是什么,使这成为一个合理的事情。

以下是一个具体的例子:

$ mkdir element
$ cd element
$ git init
Initialized empty Git repository in /home/dan/element/.git/
$ echo "one" >> one
$ git add one
$ git commit -m msg
[master (root-commit) 36dc8b0] msg
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 one
$ git checkout -b fire
Switched to a new branch 'fire'
$ echo "next line" >> one
$ git checkout master
M       one
Switched to branch 'master'
$ cat one
one
next line
$

这显然与《Git Pro Book》中所说的相矛盾:

这是一个需要记住的重要要点:Git 会将你的工作目录重置为检出的分支指向的提交快照的样子。它会自动添加、删除和修改文件,以确保你的工作副本是该分支上最后一次提交时的样子。


2
我的回答有帮助澄清问题吗?我认为它是在你的编辑之前,但我不完全确定。 - larsks
2个回答

17

无论你在哪个分支上添加文件,只有当你提交文件时才会有影响。所以如果你这样做:

git add file
git checkout master
git commit

你已将文件提交到主分支。

以下是一个完整的示例,包括输出结果。我们从一个新的代码仓库开始:

$ git init
Initialized empty Git repository in /home/lars/tmp/so/repo/.git/

此时,我们在master分支上,还没有添加任何文件。让我们添加一个文件:

$ date > file1
$ cat file1
Fri May 11 13:05:59 EDT 2012
$ git add file1
$ git commit -m 'added a file'
[master (root-commit) b0764b9] added a file
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 file1

太好了,我们现在有了一个分支(master)并且有一个提交。让我们创建新的分支:

$ git checkout -b foo
Switched to a new branch 'foo'
$ git branch
* foo
  master
$ ls
file1

现在我们将在file1中添加一行。

$ date >> file1
$ git status
# On branch foo
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   file1
#
no changes added to commit (use "git add" and/or "git commit -a")

这表明该文件已被修改,但尚未暂存。让我们将文件暂存并提交:

$ git add file1
$ git commit -m 'made a change'
[foo 761bed9] made a change
 1 files changed, 1 insertions(+), 0 deletions(-)

然后重新运行git status

$ git status
# On branch foo
nothing to commit (working directory clean)

此时,文件看起来像这样:

Fri May 11 13:05:59 EDT 2012
Fri May 11 13:07:36 EDT 2012

如果我们切换回master分支,我们会看到没有第二行的早期版本的文件:

$ git checkout master
Switched to branch 'master'
$ cat file1
Fri May 11 13:05:59 EDT 2012

对文件的更改仅限于它们所提交的分支。

在您更新的示例中,这个...

$ git checkout master

这段代码没有出现错误,是因为此时masterfire中的'version of "one"'是相同的。工作目录中的更改对任何一个版本都同样适用。


1
是的 - 如果您在提交方面以原子方式处理分支,则一切都与此示例相同,并且对我来说很有意义。如果您进行修改并可能进行暂存而不进行提交,然后切换分支,则会进入发生在我的第一篇帖子中的奇怪循环状态。道德是永远不要在没有提交的情况下切换分支?但是:如果我朝另一个方向执行此操作并在主分支上工作,然后切换到fire分支,它会给我一个错误:error: You have local changes to 'one'; cannot switch branches.不知道为什么我在另一个方向上工作时没有得到这个错误。 - djechlin
1
在运行 git add 命令之前,该文件是否实际上已被跟踪?您能否使用示例存储库展示实际命令和输出来重新构建您的问题?通常,在切换分支之前,将更改提交或存储是很常见的。 - larsks
1
修改了问题以显示完整的对话而不仅仅是命令。我会研究stash - 似乎我没有真正理解git中“浅”分支的含义。虽然我不明白为什么git让我进行了脏检出。 - djechlin
我想我明白了。只要分支指向相同的工作目录,你就可以自由切换。一旦你提交了一个分支并且它们指向不同的快照,Git 就不会让你进行脏检出。 - djechlin

9
暂存区(也称为索引)对于所有分支都是共用的,这解释了你的观察结果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接