使用GIT部署代码 - checkout vs reset --hard?

时间:2017-03-03 01:47:45

标签: git

我需要确保服务器上的某个python部署位置始终与远程分支同步,并删除服务器上的任何更改。选项1和选项2之间有什么区别?哪一个更受欢迎?我打算及时运行它以确保服务器代码与远程同步。

选项1:

git clean -f -x
git fetch --all
git reset --hard origin/master

选项2:

git clean -f -x
git fetch --all
git checkout --force origin/master

2 个答案:

答案 0 :(得分:4)

为了正确理解差异,让我们从这些定义开始:

  • Git存储库包含提交,它们是一些带有关联元数据的目录文件树()的快照,通常包括一个父提交(但允许任意数量的父提交ID)。这些提交由大丑陋的哈希ID deadc0defeedbeefac0ffee...等标识。因为这些丑陋的哈希ID很大,很丑,看似随意,我们使用名称 - 分支和标记名称,主要是为了跟踪它们。

    存储在存储库中的文件只有一个对Git本身有用的表单。普通命令无法读取它们。 (只有Git可以这些存储的对象,并且它们具有一次写入形式:一旦写入,它们就永远不会被改变。对于所有四种Git对象都是如此,但是的分支名称,假定要更改:每次向分支添加新提交时,分支名称下存储的哈希ID都会更改。)

  • 每个存储库还有一个索引。索引 - 注意有一个特殊的,区别的"" index-是您构建要创建的 next 提交的位置。它开始匹配一些现有的提交。该索引具有多个角色,包括使Git快速(其缓存角色),以及处理合并和安排下一次提交(其临时区域角色)。但它开始匹配一些现有的提交。

    索引本质上是一个扁平化的树,因此它就像以特定方式提交的内容。与内部Git对象一样,它的形式仅对Git有用。

  • 大多数存储库还有一个(单个,可分辨的)工作树,您可以在其中处理文件。与索引一样,树开始匹配某些提交。因为这些是普通文件,计算机的其余部分可以处理,但是,它们既可以写入也可以读取,或者您可以添加新文件或删除文件。

    如果更改工作树中的文件,并且想要提交新版本,则必须将文件从工作树复制到索引中。这就是git add的作用:它将文件复制到索引中,或者替换现有文件 - 如果文件的路径名在那里之前 - 或者存储一个全新的路径名(如果路径 - 名字是新的。)

    同样,要从索引中删除文件,您必须运行git rm。默认情况下,这将从 索引工作树中删除文件(但您可以告诉它单独保留工作树版本)。

    < / LI>
  • 存储库始终 1 有一个当前提交。这称为HEAD提交。 .git/HEAD(它是.git目录中的普通文件)真正起作用的方式是它通常只包含当前分支名称。实际的提交ID存储在分支名称下。然而,Git有所谓的&#34;分离的HEAD&#34;模式,其中HEAD包含哈希ID。对于git checkoutgit reset来说,这一切都很重要。

因此,有一个当前提交,通常来自当前分支名称,另外还有一个分支名称实际上只是查找名称的方式 - to-ID映射以查找提交。我们称之为HEAD,它是以下内容的缩写:&#34;嘿,Git,读取.git/HEAD,找到当前分支名称,并使用它来查找当前提交。或者,如果我处于分离的HEAD模式,请像以前一样阅读.git/HEAD,但要看到它有一个哈希ID,然后那个是当前的提交&#34;

现在,您在此处查看的两个不同命令是git resetgit checkout。这些目标非常不同

  • git reset(主要是 2 )关于以某种方式更改当前分支名称的名称到ID映射,还可以选择更改索引,甚至更改所有三个:当前提交,索引,工作树。

  • git checkout(主要)关于更改哪个分支是当前分支,通过将新的分支名称写入HEAD,或者通过&#34;分离&#34; HEAD:将提交哈希ID写入HEAD。在此过程中,git checkout 更改索引和工作树。

这是事情变得有点复杂的地方。 :-)由于您使用--hardgit reset 更新索引和工作树。所以现在听起来它更像git checkout,在某些方面它是。但是存在一些重要的差异:

  1. 使用提交当前分支点的git reset 更改。使用git checkout更改哪些提交是最新的。

    请记住,我们上面说过像masterdevelop这样的名称是我们让Git记住大丑陋哈希ID的方式。结果是这些名称就像标签,粘贴或指向特定提交。 git reset命令可让您移动标签:更改指向的位置;将其剥离一个特定的提交并粘贴到另一个。相比之下,git checkout 不会移动标签。相反,它会更改哪个分支名称存储在.git/HEAD中。您git checkout master切换到分支master,然后git checkout develop切换到分支develop。标签保持原样,但Git会更改.git/HEAD中存储的名称

    所有这些都适用于正常情况,当你在分支上时#34;。如果你在&#34;分离的HEAD&#34; case,git reset仍然将你的分离HEAD从一个提交移动到另一个提交 - 但由于没有涉及分支 name ,这不会改变任何现有的分支名称。同样,如果你git checkout某个不是分支名称的东西,git checkout&#34;分离你的HEAD&#34;将原始提交ID写入.git/HEAD

    请注意git reset永远不会分离HEAD并且永远不会重新附加HEAD。这是git checkout所做的事情。同时,git checkout永远不会更改任何分支标签,但git reset会更改。

  2. git checkout命令尝试非破坏性(无论如何都是这种模式;再次参见脚注2)。 git reset命令愉快地破坏未保存的工作

    实际上,这意味着如果您对索引和/或工作树进行了更改,git checkout将不会覆盖它们。这变得特别复杂,因为它切换分支或提交,如果可以的话。它是通过将未保存的工作保留在索引和/或工作树中,如果可以的话。如果它不能,则只会出错。

    相比之下,git reset --hard会丢弃这些未保存的更改:--hard表示会覆盖索引和工作树。

  3. 在评论中,您添加了:

      

    我想忽略任何人可能在服务器上做出的任何提交(即使这不太可能)

    但是&#34;忽略&#34;与&#34; discard&#34;不同,并且这些都不会解决对索引和/或工作树所做的未提交的更改。

    通常情况下,对这样的集中式服务器做正确的事就是设置没有工作树,即是--bare存储库。没有工作树的存储库可能无法完成工作。首先,没有什么可以保存,可能忽略,或者可能丢弃。如果使用这种方法,所有这些区别都会消失。这是你最好的选择。

    即使您不能或不会使用--bare存储库,也请查看剩余的项目。除了上述所有考虑因素之外,请记住,集中存储库 - 某人git push es 首先提交的存储库 - 仍然具有当前(HEAD)提交,如果它不分离 - 命名当前分支。它还有一个索引,如果不是裸的话,还有一个工作树。 Git通常会拒绝推送到工作树的当前分支:请参阅the git config documentation中的receive.denyCurrentBranch。由于裸存储库没有工作树,因此对于裸存储库,这个特定的Git问题也会消失。

    (但是,您的描述听起来像是将其设置为不是其他人直接推送的中央服务器,而是作为另一台服务器的客户端,您在客户端上使用git fetch。在这种情况下,下面的几个项目变成了非问题。)

    所以,让我们说你有一个非裸的存储库,有一个工作树。为了允许推送成功,您可能需要使用分离的HEAD,否则Git将拒绝推送到当前分支。 (较新的Gits能够接受它们,但您必须非常小心地使用它;有关详细信息,请参阅链接的文档。)分离的HEAD将为您提供当前提交而不使用当前分支,并将使git reset和{ {1}}表亲比你在树枝上的表亲更近。

    (它还会破坏客户在克隆中央存储库时看到的功能:客户端将检查服务器上当前的任何分支,即客户端询问服务器&#34;您的HEAD分支是什么?&#34;并自动检查。此功能有一些可疑的价值,所以你可能不会错过它,但值得注意。)

    如果您正在使用此分离的HEAD,那么git checkoutgit reset --hard origin/master之间的主要区别是未提交的索引和/或工作树修改会发生什么。使用git checkout origin/master,它们将被删除,没有任何警告。任何积极在服务器上工作的人都是SOL。将重新设置索引和工作树以匹配新提交。

    如果您使用分离的HEAD,git reset --hard会将git reset --hard origin/master的提交ID写入当前分支名称。和以前一样,origin/master也会更新索引和工作树。这不仅消除了活动工作,还通过从旧提交中剥离分支标签并将其粘贴到新提交上来抛弃在当前分支上进行的提交。

    使用git reset,这也有点复杂。由于git checkout是一个所谓的远程跟踪分支,因此检出此名称会为您提供一个分离的HEAD,并将提交哈希直接存储在origin/master中。无论先前是否附着过HEAD,都是如此。所以.git/HEAD现在分离的,前提是结帐成功。如果索引和工作树中没有未提交的更改,则结帐应 3 成功。如果未提交的更改,则checkout会在可能的情况下将它们带到新的分离的HEAD,如果在不擦除未提交的工作的情况下无法切换到该提交,则会失败。

    因此,如果您选择沿着这条路走,那么这些是您的选择。由于Git是一个大型工具集,而不是单个固定解决方案,因此如果更合适,您可以选择一些其他路径。

    (请注意,HEAD将删除未跟踪和忽略的文件,即不在索引中的内容。但是,如果没有git clean -f -x,则不会删除未跟踪的目录。)

    1 这个&#34;总是当前提交&#34;规则,主要用于新的空存储库的情况,当没有提交时。除非您在空的存储库中工作,否则您将不会遇到此异常,或者使用-d创建一个新的未出生的分支,就像Git调用它一样。

    2 git checkout --orphangit reset都有其他用法,这些用法从主要功能偏离,从一点点到多点。我只是在这里描述他们的主要功能。

    3 由于例如磁盘空间不足,git checkout仍然可能失败。

答案 1 :(得分:0)

选项1:用获取的内容替换master分支上的所有工作

选项2:只需将您切换到主分支