0

我有 3 个未暂存的文件。
我想要的是提交并推送其中的 2 个并将第三个保存在私有分支中。
如果我为其中 2 个执行 git add/git commit ,然后 git checkout 分支名称,然后 git checkout master 和 THEN git pull ,然后是 git push 我是否正确?

4

1 回答 1

1

具体而言,您在master,并且您有三个文件:

.../file1
.../file2
.../file3

其中...零件是回购中的任何路径,我将它们留在下面。

前两个文件是新的(您刚刚创建它们)或现有的(您修改了它们),在这种情况下无关紧要。

同时file3也是新的或修改的,但这可能会有所不同。

第一部分绝对是正确的:git add file1 file2; git commit将进行一个新的提交,其中包含两个新的或修改的文件(以及 repo 中已经存在的所有其他文件),但不包含file3(如果它是新的),或者不包含对file3(如果它已经存在)。

一旦使用file1and提交file2完成,您就可以推送新的master

git push origin master

file3当您执行上述操作时,您还没有做任何事情,或者您在哪个分支上都没有关系;通过指定要将本地分支推master送到origin's master,您至少可以随时尝试此推送。(这就是两个额外参数的用途。origin部分是“它要去哪里”,该master部分的含义与 相同master:master,意思是“推送本地master-> 远程master”。)或者,如果你愿意,你可以延迟这个,我们待会儿就会讲到。更重要的是,这可能会因“拒绝”消息而失败,在这种情况下,您几乎必须file3在某处提交时延迟它。

现在,至于file3,情况有点复杂。您想“将其保存在私有分支中”,好吧,我们可以这样做,但是:它是一个全新的文件,还是已经存在?如果全新的事情很容易。如果它已经存在,那么还有一两个问题:这个私有分支是分支,还是现有(私有)分支?如果它是一个现有的分支,那么文件在“你现在所在的任何分支”和“你想要它的另一个分支”之间是否有所不同?

对于新分支或新文件,这里不会有任何问题。做就是了:

git checkout -b new_private_branch    # create new private branch
git add file3
git commit

或者:

git checkout existing_private_branch
git add file3
git commit

如果您现在所在的任何分支与现有的私有分支之间没有变化,那么第二个命令也将起作用。但是,如果file3在您当前的分支中,并且也在 中existing_private_branch,并且两者之间不同,您将获得:

error: ... would be overwritten by checkout:
    file3

当您尝试移动到现有的私有分支时。这是因为 git 需要覆盖工作树版本file3才能移动到该分支。

如果是这种情况,您需要将修改的file3(或对 的更改file3)保存在git checkout 不会覆盖的地方,然后使工作目录file3干净或不存在。有许多方法可以做到这一点(例如,mv file3 another_namegit stash),具有各种不同的优点和缺点。

我猜file3是新的,和/或私有分支是新的,所以这里没有必要详细介绍。无论如何,让我们假设您已正确保存file3private_branch. 这意味着您git commit在该私有分支上做了一个并git status表明一切都是干净的。

git pull、git fetch、git merge 和/或 git rebase

一切都在本地清理干净,您现在可以执行以下操作:

git checkout master

回到主人身上。

如果其他人打败了你git push,而你git push origin master现在尝试(而不是更早),你会得到一个错误:

 ! [rejected]        master -> master (non-fast-forward)

这意味着,他们打败了你。他们把一些master你还没有的东西放进去。你需要得到它,你如何得到它git fetch

如果你没有得到一个rejected,没有人能打败你,你的状态很好,你就完蛋了!所以你不需要任何pullfetch-然后-merge或任何东西。如果你git push origin master早一点这样做,你更有可能击败其他人。但是,如果您之前尝试过但失败了,或者您现在尝试但失败了,那么现在是时候做点什么了。

git 拉

git pull命令实际上意味着git fetch跟随git merge(除非您将其配置为进行变基)。我不喜欢使用git pull太多——我喜欢单独fetch的 -then-decide 步骤。即使您仍然使用git pull,让我们将其分解为fetchmerge部分。

获取

我也喜欢在origin这里输入部分:

git fetch origin

除非你已经配置了一堆 git,否则无论如何fetch都会假设origin,所以你可以省略origin部分;我只是喜欢明确。

这就是提交的 ASCII 图表派上用场的地方。在第一次git push尝试失败(被“拒绝”)并且您执行 agit fetch之后,这就是您最终的结果。每个字母代表一些提交。提交A并且B是你开始的。CommitCfile1and所做的file2。提交D是“他们”(无论谁打败你)所做的。您的私有分支也“从 B 中脱落”,但我不会费心在这里画它。

... - A - B - D       <-- origin/master
            \
              C        <-- master, HEAD=master

推送被“拒绝”,因为您要求遥控器移动origin/master到指向C. From C,历史可以追溯到B,而 git 只“向上和/或向后”遵循这些行,因此没有来自Bto的链接D。那会让所有的承诺D变得孤独和被遗忘。

此时,您有两个简单的(ish)选项。你可以git merge。既然你现在就在master轨道masterorigin/master,这意味着与长形式相同:

git merge origin/master

这告诉 git 将当前的分支(即master)与您刚刚“获取”-ed 的分支合并origin/master。如果合并顺利,它会自动git commit-s 合并,现在你有了远程会满意的东西。从图表上看,它使这个新的提交 E:

... - A - B - D        <-- origin/master
            \   \
              C - E    <-- master, HEAD=master

现在git push origin master会成功的。指向origin/master是可以的E因为E指向 。但是,这确实意味着你已经做了一个你本可以避免的合并提交。你是否应该避免它是一个政策决定,而不是一个技术决定(所以除了你的老板之外没有人可以告诉你以这样或那样的方式做这件事:-))。C D

您的另一个选择是使用git rebase而不是git merge

git rebase origin/master

(与 一样git merge,您实际上可以省略origin/master部分,因为master正在跟踪origin/master。)

这样做的目的是获取您之前所做的提交——添加file1file2——并“找出在该过程中发生了哪些变化”(即,您在这些文件中所做的更改),然后对origin/master. 如果一切顺利,它会留下你的旧提交(它仍然存在,git 将所有内容保存很长时间)并进行的提交。结果是这样的:

... - A - B - D        <-- origin/master
           \    \
            \     C2   <-- master, HEAD=master
             \
              C        [no label]

old-commit- C(file1file2) 中的更改现在也在 new-commit-C2中。不像CC2是“在上面” origin/master。所以现在git push origin master会成功,每个人(包括你)最终都会忘记一切C,只是使用C2,历史看起来就像A - B - D - C2. 它并没有真正比合并版本“更好”,但它更易于查看和(有时)考虑。

于 2013-08-13T14:01:35.777 回答