git 中存储的每个版本(“提交”)都形成一个图形,通常有助于从该图中思考你在 git 中所做的操作。
当用户A开始时,假设只创建了两个提交,我们将它们称为P
和Q
:
P--Q (master)
然后他修改了FileA,暂存该更改并创建一个提交以表示源代码的新状态 - 假设该提交名为R
。它有一个父提交,即提交Q
:
P--Q--R (master)
成功推送后,GitHub存储库的提交记录图看起来与之前相同。 UserB 的历史记录也是相同的。
P--Q (master)
... 但是他创建了一个不同的提交版本,比如称为S
,其中包含他修改过的FileB文件:
P--Q--S (master)
UserB试图将内容推送到GitHub,但推送被拒绝 - 如果不"强制"推送,你无法更新远程分支,除非你所推送的版本包含该远程分支中所有历史记录。因此,UserB从GitHub拉取了代码。拉取实际上包括两个步骤:获取和合并。获取会更新origin/master
,这类似于来自远程仓库origin
的master
分支状态的缓存。(这是"远程跟踪分支"的一个示例。)
P--Q--S (master)
\
R (origin/master)
这张图中的历史记录已经分叉了,所以合并尝试通过创建一个合并提交(比如 M
),将两个历史记录统一起来,并希望代表来自两个分支的更改:
P--Q--S--M (master)
\ /
\ /
R (origin/master)
当GitHub向您展示代表提交所引入的更改的差异时,在只有一个父提交的情况下,很容易进行差异比较 - 它只需要从那个版本开始进行差异比较。然而,在像
M
这样具有多个父级的提交中,它必须选择一个父级来显示与之进行差异比较。这就解释了为什么合并提交
M
所显示的差异可能看起来与
S
或
R
中的一个相同。在Git中,提交是通过源树的确切状态来定义的,而不是得到该状态的更改。
git pull --rebase
使历史线性化 - 在获取(fetching)后,它会尝试重新应用任何不在origin/master
中的提交。 (b) 如果您尝试合并到当前分支的提交已经包含了当前分支的历史记录,则默认情况下Git不会创建合并提交 - 它只会将您的分支"快进"到正在合并的分支状态。 - Mark Longairgit config --global branch.autosetuprebase always
命令将远程分支设置为自动执行git pull --rebase
操作。可以通过git config branch.<branch>.rebase true
来单独设置某个分支。这样可以防止引入额外的合并提交,使历史记录更加一致和规律。 - Bill Door