【问题标题】:How do I make sure a git commit to a non-master branch is not also accidentally to the master branch?如何确保对非主分支的 git 提交不会意外地对主分支?
【发布时间】:2018-07-06 22:08:12
【问题描述】:

我初始化了一个 git repo,想把所有东西都放在 dev 分支中,而不是放在 master 分支中,所以我使用了这个命令序列:

$git checkout -b dev
$git add .
$git commit -m [I added a string message]

此时 git log 显示 HEAD 在 dev。

然后我尝试用这个返回主分支:

$git checkout master

但是我得到了“master”未被识别的错误,所以我这样做了:

$git checkout -b master

现在日志显示头部在 dev 和 master 处。

当我这样做时:$git ls-tree -r --name-only master 我得到的所有文件都与使用 dev 时一样显示。

我是怎么搞砸的,如何确保我的文件没有提交到 master 分支,而只提交到 dev 分支?

【问题讨论】:

    标签: git version-control repository


    【解决方案1】:

    这一切都很正常。真的没有什么可做的(嗯,除了“在 Git 中习惯这种事情”)。

    详细说明为什么这是正常的以及这意味着什么

    我初始化了一个 git repo ...

    让我们在这里非常清楚。我假设你是这样做的,或者是相当等价的:

    $ mkdir newrepo
    $ cd newrepo
    $ git init
    Initialized empty Git repository in ...
    

    如果是这种情况,那么你的处境很有趣:

    $ git status
    On branch master
    
    No commits yet
    
    nothing to commit (create/copy files and use "git add" to track)
    $ git branch
    $ 
    

    也就是说,虽然你是 on branch master,正如 git status 所说,但分支 master 实际上并不存在你在一个还不是分支的分支上!

    所以我使用了这个命令序列:

    $ git checkout -b dev
    $ git add .
    $ git commit -m "some message..."
    

    这一切都很好,但是让我们看看git checkout -b之后会发生什么:

    $ git checkout -b dev
    Switched to a new branch 'dev'
    $ git status
    On branch dev
    
    No commits yet
    
    nothing to commit (create/copy files and use "git add" to track)
    $ git branch
    $ 
    

    换句话说,唯一改变的是我们已经从不存在的分支master变成了不存在的分支dev

    现在我们将一些文件添加到存储库并提交,我们会看到这个输出(由于各种原因,我的输出与你的会有所不同):

    $ echo example > README
    $ git add README
    $ git commit -m initial
    [dev (root-commit) 8ab55c8] initial
     1 file changed, 1 insertion(+)
     create mode 100644 README
    $ 
    

    此时git statusgit branch 的输出发生变化:

    $ git status
    On branch dev
    nothing to commit, working tree clean
    $ git branch
    * dev
    $ 
    

    由于这个 Git 相当现代,git status 已从(新的)No commits yet 输出(用于新的空存储库)切换到更典型的 nothing to commit, working tree clean 消息,这是 Git 自 Git 1.6 以来(如果不是更早)使用的消息(尽管随着时间的推移有轻微的措辞变化)。我们在分支dev 和分支dev 存在。如果我们使用所谓的管道命令,我们可以看到名称 dev 标识的哈希 ID:

    $ git rev-parse dev
    8ab55c84b79cad21440187b4f95ce7c3b947064b
    

    这是我们在git commit 输出中看到的长版本,当它说:

    [dev (root-commit) 8ab55c8]
    

    这告诉我们我们刚刚创建了一个新的提交,这个新的提交具体是一个 root 提交(没有父级),这个新提交的缩写哈希 ID 是 8ab55c8,并且新的提交导致更新——或者在这种情况下,创建——分支名称dev

    分支名称包含哈希 ID

    至少在 Git 中,分支名称只是哈希 ID 的人类可读名称,并具有其他特殊属性。定位提交的是哈希 ID,而不是名称:name 用于查找哈希 ID。在相当真实的意义上,哈希 ID 提交;只有一些数据该哈希 ID 相关联,我们可以在这里看到:

    $ git cat-file -p 8ab55c8 | sed 's/@/ /'
    tree 5c7082135e61da9ffc72ae2cd7d29fe702315004
    author Chris Torek <chris.torek gmail.com> 1517087854 -0800
    committer Chris Torek <chris.torek gmail.com> 1517087854 -0800
    
    initial
    

    这些数据为我们提供了——或者更确切地说是 Git——Git 需要找到与提交相关的文件(通过一个稍微漫长而曲折的过程)所需的一切:

    $ git cat-file -p 5c70821
    100644 blob 33a9488b167e4391ad6297a1e43e56f7ec8a294e    README
    $ git cat-file -p 33a9488
    example
    

    但这里的关键是一切都通过哈希 ID 起作用; dev 这个名字只是用来定位第一个——或者我们应该说last——哈希ID。 (Git 喜欢逆向工作:一切都是最新的或最新的,如果需要,我们会从那里找到更早的东西。这不仅对我们 使用 Git 更方便,而且也是必需 em> 因为所有内容一旦存储,就是完全只读的。)

    添加一个 new 提交包括写出提交(这需要首先收集其数据并写出其树),然后使当前分支名称指向新提交。新提交一经创建,就会包含提交的哈希 ID, 是分支的尖端。 (新提交的哈希 ID 是在现场创建的,来自进入新提交的所有数据。)这是分支名称的另一个特殊功能:当你“打开”一个特定的分支,添加一个新提交告诉 Git 将新提交的哈希 ID 写入分支名称。

    这意味着要存在一个分支名称,它必须包含一个哈希 ID

    此时您尝试执行git checkout master,但没有名为 master 的分支。所以你跑了:

    git checkout -b master
    

    告诉 Git 继续并创建名称master,指向当前提交。

    当前提交是存储库中的(一个,单个)提交,并且名称 dev 已经指向那个提交。所以现在你有两个分支名称,devmasterboth 都指向那个提交:

    o   <-- dev, master (HEAD)
    

    git checkout 部分将 Git 的“哪个分支是当前分支”的概念转换为 mastercommit 已经存在; 分支名称是新的(由-b创建); HEAD 现在附加到新的分支名称。

    如果您现在进行新的提交,Git 将构造新的提交,它会获得一个新的哈希 ID,以便新的提交记住——“指向”——当前的提交。然后它将新提交的哈希 ID 写入master

    o   <-- dev
     \
      o   <-- master (HEAD)
    

    现在master 将有两个提交,dev 将有一个。

    为了合并分支(稍后),两个分支应该有一些共同的提交。现代 Git 不会在没有 --allow-unrelated-histories 的情况下合并不相关的分支。合并不相关的历史也可能有点棘手。这不是您第一次合并时想要做的事情。旧版本的 Git 会在没有警告的情况下合并不相关的历史记录(create 仍然有点棘手)。所以虽然有可能,但这不是你应该做的事情。

    【讨论】:

    • 非常感谢您的解释!我很难找到我能理解的解释,部分原因是不知道谷歌的术语。但听起来我从master中删除master中的文件是不安全的,所以它们只在dev中。
    • @MickeyLater:最好根据提交(而不是文件或分支)来考虑这一点。 this 提交包含什么内容?你想在 next 提交中做什么?当您最终运行git merge 时,它会在提交的基础上运行。分支名称主要是为了帮助您记住原始提交哈希 ID,否则这些 ID 是不可理解且无用的(至少对人类而言)。
    • 提交 1 已在 dev 分支上添加和更新文件,但我检查并保持 master 分支不变。提交 2 从 master 分支中删除了不需要的文件,我检查它没有做任何事情开发。我知道我仍在提出分支,但不知道如何避免它。
    • 你原来的问题没有提到第二次提交。添加第二个提交后,您将更新两个分支名称之一以指向新提交。新提交的内容将是您运行git commit 时索引中的任何内容。如果您稍后运行 git merge 并且它需要进行完全合并,Git 将找到 merge base 提交,并(实际上)运行两个 git diff 命令。如果一个 diff 看到“文件已删除”而另一个 diff 看到“这些文件没有更改”,Git 将通过删除合并结果中的文件来合并这两个更改(这可能是您想要的)。
    【解决方案2】:

    首先,解释一下你遇到这种情况的原因。

    1. 为什么第一次使用git checkout master时master分支没有识别出来

      当你初始化一个 git repo 时,默认的分支名称是master,并且master 分支上没有任何版本(提交)。您没有在 master 分支上提交更改,而是切换到 dev 分支 (git checkout -b dev) 并在 dev 分支上提交了更改。这意味着丢弃master 分支并改用dev 分支并在dev 分支上提交更改。如果你使用git branch,它只会列出当时的dev分支。

    2. 为什么masterdev 分支显示相同

      当您在dev 分支上执行命令git checkout -b master 时,git 将从HEAD(在dev 分支上)所在的位置创建新分支master,然后将HEAD 切换到新分支创建分支master。所以master 也将指向你在dev 分支上提交的提交。

    在大多数情况下,分支通常具有相同的祖先。如果您不希望master 分支与dev 分支具有相同的祖先,您可以将master 分支更改为孤立分支(与dev 分支无关的历史记录):

    git checkout dev
    git branch -D master
    git checkout --orphan master
    # make changes as you need
    git add .
    git commit -m 'commit changes on master branch'
    

    【讨论】:

      猜你喜欢
      • 2012-01-25
      • 1970-01-01
      • 2018-09-09
      • 1970-01-01
      • 1970-01-01
      • 2012-08-22
      • 1970-01-01
      • 1970-01-01
      • 2014-09-19
      相关资源
      最近更新 更多