在什么情况下,“拉扯”会有害? [英] In what cases could `git pull` be harmful?

查看:81
本文介绍了在什么情况下,“拉扯”会有害?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一位同事声称 git pull 是有害的,只要有人使用它就会感到不安。



git pull 命令似乎是更新本地存储库的标准方式。是否使用 git pull 创建问题?它创造了什么问题?有没有更好的方式来更新一个git仓库?

解决方案

总结



默认情况下, git pull 会创建合并提交,这会增加代码历史记录的噪音和复杂性。另外, pull 可以让您不用考虑您的更改如何受到传入更改的影响。



只要它只执行快进合并, git pull 命令是安全的。如果 git pull 被配置为仅执行快进合并,并且无法进行快进合并,则Git将退出且出现错误。这将使您有机会研究传入的提交,考虑它们可能会如何影响您的本地提交,并决定最佳的操作过程(合并,重定位,重置等)。

使用Git 2.0及更新版本,您可以运行:

  git config --global pull.ff only 

可将默认行为更改为仅快进。 Git版本在1.6.6和1.9.x之间,你必须养成打字的习惯:

  git pull  - -ff-only 

然而,对于所有版本的Git,我建议配置一个 git up 别名如下:

  git config --global alias.up'!git remote update -p; git merge --ff-only @ {u}'

并使用 git up 而不是 git pull 。因为:




  • 它更喜欢 git pull --ff-only 的别名适用于所有(非古老的)Git版本,
  • 它可以获取所有上游分支(而不仅仅是您当前正在使用的分支),以及
  • 清除旧的 origin / * 不再存在的分支。

    git pull



    git pull 如果使用得当,它并不坏。最近对Git的一些更改使得更容易正确使用 git pull ,但不幸的是,默认的 git pull >有几个问题:


    • 它在历史中引入了不必要的非线性因素

    • 意外重新提交上游有意重新提交的提交

    • 以不可预知的方式修改您的工作目录

    • 暂停您正在做的工作以审查其他人的工作讨厌使用 git pull

    • 它使得很难正确重新绑定到远程分支上

    • 它不清理远程仓库中删除的分支



    这些问题将在下面详细介绍。



    非线性历史记录



    默认情况下, git pull 运行 git fetch ,然后执行 git merge @ {u} 。如果本地存储库中存在未提交的提交,则 git pull 的合并部分会创建合并提交。



    对合并提交没有任何固有的坏处,但它们可能是危险的,应该予以尊重:


    • 合并提交本质上很难检查。要了解合并的内容,您必须了解所有父母的差异。传统的差异不能很好地传达这种多维信息。相比之下,一系列正常的提交很容易检查。

    • 合并冲突解决方案非常棘手,并且错误经常长期未被发现,因为合并提交很难查看。 >
    • 合并可以悄悄地取代常规提交的影响。代码不再是增量提交的总和,导致对实际改变的误解。

    • 合并提交可能会破坏一些持续集成方案(例如,仅自动构建第一个父路径根据假定的惯例,第二位父母指出进行中的不完整作品)。



    当然,有时间和地点进行合并,但理解合并时应该和不应该使用可以提高你的仓库的有用性。



    请注意,Git的目的是使分享和消费进化的代码库,而不是精确记录它展开的历史记录。 (如果您不同意,请考虑 rebase 命令及其创建原因。)由 git pull 创建的合并提交不会将有用的语义传达给其他人 - 他们只是说在你完成更改之前别人碰巧推送到存储库。为什么这些合并提交,如果他们对别人没有意义,并可能是危险的?



    可以配置 git pull 来代替合并,但这也有问题(稍后讨论)。相反,应将 git pull 配置为只进行快速合并。 / h3>

    假设有人重组了一个分支并强制推送它。通常不会发生这种情况,但有时也是必需的(例如,删除意外排列和推送的50GiB日志文件)。由 git pull 完成的合并将会将新版本的上游分支合并到仍然存在于本地存储库中的旧版本中。如果你推动结果,高音叉和火把将开始以你的方式。

    有些人可能会争辩说,真正的问题是强制更新。是的,通常建议尽可能避免推力,但它们有时是不可避免的。开发人员必须准备好处理强制更新,因为它们有时会发生。这意味着通过一个普通的 git pull

    惊喜工作目录修改



    在完成 git pull 之前,没有办法预测工作目录或索引的外观。有可能会有合并冲突,你必须解决之前,你可以做任何事情,它可能会在工作目录中引入50GiB日志文件,因为有人不小心推动它,它可能会重命名你正在工作的目录等。


    $ b

    git远程更新-p (或 git fetch --all -p )允许您在决定合并或重新绑定之前查看其他人的提交,从而允许您在采取行动之前制定计划。



    难度审查其他人的提交 h3>

    假设您正在进行一些更改,而其他人希望您查看他们刚刚推送的某些提交。 git pull 的merge(或rebase)操作会修改工作目录和索引,这意味着您的工作目录和索引必须是干净的。



    您可以使用 git stash ,然后使用 git pull ,但是当您重新审查?要回到你所在的位置,你必须撤消由 git pull 创建的合并并应用存储。



    git remote update -p (或 git fetch --all -p )不会修改工作目录或索引,所以即使您已经进行了暂停和/或暂停的更改,也可以随时运行。你可以暂停你正在做的事情,并检查别人的提交,而不必担心隐藏或完成你正在进行的提交。 git pull 并不能为您提供这种灵活性。

    重新加入远程分支



    一个常见的Git使用模式是做一个 git pull 来引入最新的变化,后面跟着一个 git rebase @ {u} 来消除引入了 git pull 的合并提交。 Git有一些配置选项可以通过告诉 git pull 来执行rebase而不是合并(参见<$ c $)来将这两个步骤简化为单个步骤c> branch。< branch> .rebase , branch.autosetuprebase pull.rebase master ),既没有分叉拉( git pull 分支。 .rebase 设为 true ),也不是合并拉(默认 git pull 行为)将工作。这是因为 git rebase 不使用 - preserve-merges 选项就消除了合并(它使DAG线性化)。无法将rebase-pull操作配置为保留合并,并且紧跟着 git rebase -p @ {u} 的合并拉不会消除引起的合并通过合并拉。 更新: Git v1.8.5添加了 git pull --rebase = preserve git config pull.rebase preserve 。这些会导致 git pull 在获取上游提交之后执行 git rebase --preserve-merges 。 (感谢 funkaster 的提醒!)



    清理已删除的分支



    git pull 不修剪远程跟踪分支,对应于从远程删除的分支库。例如,如果有人从远程仓库删除分支 foo ,您仍然会看到 origin / foo 。 p>

    这会导致用户意外复活死亡分支,因为他们认为他们仍然活跃。

    更好的选择:使用 git up 而不是 git pull



    code> git pull ,我建议创建并使用下面的 git up 别名:

      git config --global alias.up'!git remote update -p; git merge --ff-only @ {u}'

    这个别名下载所有最新的提交所有上游分支(修剪死亡分支)并尝试将本地分支快速转发到上游分支上的最新提交。如果成功,那么没有本地提交,所以不存在合并冲突的风险。快速前进将失败,如果有本地(unpushed)提交,让您有机会在采取行动之前检查上游提交。

    这仍然会修改您的工作目录不可预知的方式,但前提是您没有任何本地更改。不同于 git pull git up 永远不会让您提示您期望修复合并冲突。



    另一个选项: git pull - 只限 - 全部-p



    以下是上述 git up 别名的替代方案:

      git config --global alias.up'pull --ff-only --all -p'

    此版本的 git up 具有与前面的 git up 别名相同的行为,除了:




    • 如果您的本地分支没有配置上游分支,则错误消息会更隐蔽

    • 它依赖一个未公开的特性( -p 参数,它被传递给 fetch ),将来可能会改变版本的Git



    如果您正在运行Git 2.0或更新版本



    Git 2.0和更新的版本,你可以配置 git pull 来仅执行defa的快速合并ult:

      git config --global pull.ff only 

    这会导致 git pull 的行为像 git pull --ff-only ,但它仍然不会获取所有上游提交或清除旧的 origin / * 分支,所以我仍然更喜欢 git up code>。


    I have a colleague who claims that git pull is harmful, and gets upset whenever someone uses it.

    The git pull command seems to be the canonical way to update your local repository. Does using git pull create problems? What problems does it create? Is there a better way to update a git repository?

    解决方案

    Summary

    By default, git pull creates merge commits which add noise and complexity to the code history. In addition, pull makes it easy to not think about how your changes might be affected by incoming changes.

    The git pull command is safe so long as it only performs fast-forward merges. If git pull is configured to only do fast-forward merges and a fast-forward merge isn't possible, then Git will exit with an error. This will give you an opportunity to study the incoming commits, think about how they might affect your local commits, and decide the best course of action (merge, rebase, reset, etc.).

    With Git 2.0 and newer, you can run:

    git config --global pull.ff only
    

    to alter the default behavior to only fast-forward. With Git versions between 1.6.6 and 1.9.x you'll have to get into the habit of typing:

    git pull --ff-only
    

    However, with all versions of Git, I recommend configuring a git up alias like this:

    git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
    

    and using git up instead of git pull. I prefer this alias over git pull --ff-only because:

    • it works with all (non-ancient) versions of Git,
    • it fetches all upstream branches (not just the branch you're currently working on), and
    • it cleans out old origin/* branches that no longer exist upstream.

    Problems with git pull

    git pull isn't bad if it is used properly. Several recent changes to Git have made it easier to use git pull properly, but unfortunately the default behavior of a plain git pull has several problems:

    • it introduces unnecessary nonlinearities in the history
    • it makes it easy to accidentally reintroduce commits that were intentionally rebased out upstream
    • it modifies your working directory in unpredictable ways
    • pausing what you are doing to review someone else's work is annoying with git pull
    • it makes it hard to correctly rebase onto the remote branch
    • it doesn't clean up branches that were deleted in the remote repo

    These problems are described in greater detail below.

    Nonlinear History

    By default, the git pull command is equivalent to running git fetch followed by git merge @{u}. If there are unpushed commits in the local repository, the merge part of git pull creates a merge commit.

    There is nothing inherently bad about merge commits, but they can be dangerous and should be treated with respect:

    • Merge commits are inherently difficult to examine. To understand what a merge is doing, you have to understand the differences to all parents. A conventional diff doesn't convey this multi-dimensional information well. In contrast, a series of normal commits is easy to review.
    • Merge conflict resolution is tricky, and mistakes often go undetected for a long time because merge commits are difficult to review.
    • Merges can quietly supersede the effects of regular commits. The code is no longer the sum of incremental commits, leading to misunderstandings about what actually changed.
    • Merge commits may disrupt some continuous integration schemes (e.g., auto-build only the first-parent path under the assumed convention that second parents point to incomplete works in progress).

    Of course there is a time and a place for merges, but understanding when merges should and should not be used can improve the usefulness of your repository.

    Note that the purpose of Git is to make it easy to share and consume the evolution of a codebase, not to precisely record history exactly as it unfolded. (If you disagree, consider the rebase command and why it was created.) The merge commits created by git pull do not convey useful semantics to others—they just say that someone else happened to push to the repository before you were done with your changes. Why have those merge commits if they aren't meaningful to others and could be dangerous?

    It is possible to configure git pull to rebase instead of merge, but this also has problems (discussed later). Instead, git pull should be configured to only do fast-forward merges.

    Reintroduction of Rebased-out Commits

    Suppose someone rebases a branch and force pushes it. This generally shouldn't happen, but it's sometimes necessary (e.g., to remove a 50GiB log file that was accidentally comitted and pushed). The merge done by git pull will merge the new version of the upstream branch into the old version that still exists in your local repository. If you push the result, pitch forks and torches will start coming your way.

    Some may argue that the real problem is force updates. Yes, it's generally advisable to avoid force pushes whenever possible, but they are sometimes unavoidable. Developers must be prepared to deal with force updates, because they will happen sometimes. This means not blindly merging in the old commits via an ordinary git pull.

    Surprise Working Directory Modifications

    There's no way to predict what the working directory or index will look like until git pull is done. There might be merge conflicts that you have to resolve before you can do anything else, it might introduce a 50GiB log file in your working directory because someone accidentally pushed it, it might rename a directory you are working in, etc.

    git remote update -p (or git fetch --all -p) allows you to look at other people's commits before you decide to merge or rebase, allowing you to form a plan before taking action.

    Difficulty Reviewing Other People's Commits

    Suppose you are in the middle of making some changes and someone else wants you to review some commits they just pushed. git pull's merge (or rebase) operation modifies the working directory and index, which means your working directory and index must be clean.

    You could use git stash and then git pull, but what do you do when you're done reviewing? To get back to where you were you have to undo the merge created by git pull and apply the stash.

    git remote update -p (or git fetch --all -p) doesn't modify the working directory or index, so it's safe to run at any time—even if you have staged and/or unstaged changes. You can pause what you're doing and review someone else's commit without worrying about stashing or finishing up the commit you're working on. git pull doesn't give you that flexibility.

    Rebasing onto a Remote Branch

    A common Git usage pattern is to do a git pull to bring in the latest changes followed by a git rebase @{u} to eliminate the merge commit that git pull introduced. It's common enough that Git has some configuration options to reduce these two steps to a single step by telling git pull to perform a rebase instead of a merge (see the branch.<branch>.rebase, branch.autosetuprebase, and pull.rebase options).

    Unfortunately, if you have an unpushed merge commit that you want to preserve (e.g., a commit merging a pushed feature branch into master), neither a rebase-pull (git pull with branch.<branch>.rebase set to true) nor a merge-pull (the default git pull behavior) followed by a rebase will work. This is because git rebase eliminates merges (it linearizes the DAG) without the --preserve-merges option. The rebase-pull operation can't be configured to preserve merges, and a merge-pull followed by a git rebase -p @{u} won't eliminate the merge caused by the merge-pull. Update: Git v1.8.5 added git pull --rebase=preserve and git config pull.rebase preserve. These cause git pull to do git rebase --preserve-merges after fetching the upstream commits. (Thanks to funkaster for the heads-up!)

    Cleaning Up Deleted Branches

    git pull doesn't prune remote tracking branches corresponding to branches that were deleted from the remote repository. For example, if someone deletes branch foo from the remote repo, you'll still see origin/foo.

    This leads to users accidentally resurrecting killed branches because they think they're still active.

    A Better Alternative: Use git up instead of git pull

    Instead of git pull, I recommend creating and using the following git up alias:

    git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
    

    This alias downloads all of the latest commits from all upstream branches (pruning the dead branches) and tries to fast-forward the local branch to the latest commit on the upstream branch. If successful, then there were no local commits, so there was no risk of merge conflict. The fast-forward will fail if there are local (unpushed) commits, giving you an opportunity to review the upstream commits before taking action.

    This still modifies your working directory in unpredictable ways, but only if you don't have any local changes. Unlike git pull, git up will never drop you to a prompt expecting you to fix a merge conflict.

    Another Option: git pull --ff-only --all -p

    The following is an alternative to the above git up alias:

    git config --global alias.up 'pull --ff-only --all -p'
    

    This version of git up has the same behavior as the previous git up alias, except:

    • the error message is a bit more cryptic if your local branch isn't configured with an upstream branch
    • it relies on an undocumented feature (the -p argument, which is passed to fetch) that may change in future versions of Git

    If you are running Git 2.0 or newer

    With Git 2.0 and newer you can configure git pull to only do fast-forward merges by default:

    git config --global pull.ff only
    

    This causes git pull to act like git pull --ff-only, but it still doesn't fetch all upstream commits or clean out old origin/* branches so I still prefer git up.

    这篇关于在什么情况下,“拉扯”会有害?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆