分级和远程之间的Git盲点 [英] Git blind spot between staging and remote

查看:178
本文介绍了分级和远程之间的Git盲点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我相对较新的git来自VSS / TFS / SVN背景。我在Visual Studio 2015中使用了git插件。



因此,我克隆了一个存储库并对代码文件进行了一些更改。然后我看看更改选项卡(我认为在后台执行git diff),并看到我所做的更改。所有罚款和花花公子。

然后我将更改提交到本地回购,之后我对本地文件做了一些更改,查看更改并再次提交。



我的问题是,我的本地回购和远程回购之间存在盲点 - 我已经失去了本地和远程差异的可视性。自从我开始使用git以来,这一直令我感到困惑。



我这样做是否全部错误?afaikgit diff显示了我的本地工作代码库和本地回购库之间的变化。如何轻松查看本地仓库中已经积累的更改与远程仓库中的代码之间的差异?

解决方案

您需要diff 提交。特别是,您需要将当前提交(即另一个存储库中提示最多的提交)与自己的存储库中的提示最多提交进行比较。但是在最常见的情况下,直到您将该提交副本<其他存储库的提交副本您的存储库复制完成后才能这样做。这是因为您总是在自己的存储库中工作,除了将存储库连接到具有第二个存储库的第二个Git的简短周期外。



这可能是视觉上最好的说明(它需要显示分支的图形表示,至少在物理实现方面,Git与SVN相比有很大不同)。考虑这张图:

  o  -  o  -  U<  - 您提交
/
...-- o - o - B [在开始时与另一个存储库共享]
\
o - T < - 当您没有查找时提交它们提交

我给了三个最有趣的提交的信件(其他的只是 o 节点)。较新的承诺朝向正确。您提交的提交都在最前面。承诺他们所做的事情,你必须在进行比较之前提交,这就是底线。在开始所有这些工作之前,确保你和他们之间有共同点。



我相信你在问你如何比较 U (对于我们或oUrs)的内容与 T (对于他们)的内容。要做到这一点的方法是使用 git diff 加上两个提交 U Ť。但请注意,您可以比较任何提交对,例如 B (对于Base)vs U code>和 B vs T 。当你将你的工作与他们的工作合并时,Git会自动比较 B -vs - U B -vs - T



get 提交的最后一行必须运行 git fetch



如果你是来自SVN,很多这些概念本身都是陌生的。



Git vs SVN:一个30,000英尺的概览



我会在评论中扩充一些内容,但仍坚持高层观点 - 尽可能避免提及SVN实施方式非常不同的分支机构。有关Git分支的更深入快速介绍,请参阅 https://stackoverflow.com/a/44081446/1256452 (回答需要澄清拉动git分支)。



一repository vs many repository



在SVN中,只有一个实际的存储库。该存储库持有每一次提交,在某个服务器上远离,而不是在计算机上。为了解决某些问题,您可以从存储库中获取一堆代码。这将代码放在您自己的机器上,您可以在其上进行操作。你做了一些工作,当你准备好时,你告诉你的机器:


  • 显示我有什么,与服务器上的内容相比

  • 发送我已经返回到服务器的提交信息



Git用法非常不同。不只有一个存储库。每个用户有一个存储库,或者每个用户可能甚至多于一个存储库(您可以根据需要制作尽可能多的克隆,而每个存储库本身就是一个存储库)。您的存储库有每一次提交,其他所有副本(每个克隆)也一样。当然,每个克隆的起始部分与原始部分相同,但是随着时间的推移,它们会分开。



由于存在单独的存储库克隆,因此您需要SVN不需要的东西:同步两个存储库。在Git中,这些被称为 push fetch 。 (你可能认为它们应该是push和 pull ,而在Mercurial中,它们 ,但Git最初是一种超定义的 pull 以备份并引入 fetch 。不要立即使用Git的 pull ,它会将两个不同的想法混合在一起并导致你误入歧途。)

请记住,无论何时使用这些操作,都有两个 Gits与两个存储库有关。这些仓库中的每一个都是一个完整的仓库,拥有自己的全套提交以及命名这些提交的方式(Git将这些分支和标签称为目标,而且目的是相似的,但这些都与SVN的 - 你最好通过考虑,最初是关于提交而不是分支)。当你第一次创建自己的时候克隆,或者做一个 git fetch 来让你的克隆从它的源头更新 - 巧妙地命名为 origin - 你的Git所做的是重命名他们的Git的提交。由于您刚克隆,或者只是运行 git fetch ,您的存储库现在与相同,或 的超集>,他们。你现在拥有了他们所做的一切,再加上也许更多一些。



因此,当你想知道你有什么他们不需要 - 特别是,你最近的提交与他们的比较 - 你只需要做:

  git fetch origin#让我自己最新的东西

然后:

  git diff< their-latest-commit> <我 - 最新提交> 

比较他们的最高版本。



< h3>修订编号:为什么Git哈希ID很奇怪

这里有一个单独的问题,我不会深入,但会涉及到。在SVN中,最高版本很容易辨别,因为只有一个版本库,修订版本按顺序编号。如果你的修订版本是128号,而他们的号码是127号,那么显然你的修订版比他们的要晚,但是如果你的修订版是128号和129号,显然他们的时间比你的晚。但是在Git中,当你 git fetch 时,你有两个 存储库。如果您在克隆时发生了127次提交,但是您和都添加了一次提交?现在你们都有128次提交。然后你将他们的Git交叉连接到你的Git,并在他们的新提交中抽出,现在你有129个提交,他们有128个提交。会发生什么?

发生的第一件也是最重要的事情是提交没有按顺序编号,所以你的Git和他们的Git不必争取谁可以称之为128版本。相反,Git提交ID很大,丑陋,不可理解哈希ID 。确切的哈希ID是Git在进行提交时分配的 。 Git保证你的新提交的你的哈希ID不会与它们的它们的它们的 的新哈希ID冲突。



这首先允许您交叉连接两个Gits:最初,您的Git和他们的Git只交换他们最新的哈希ID。这些,加上使用有向无环图属性的算法技巧,意味着你的Git和他们的Git可以快速确定需要复制的提交(和其他Git对象)以完成提取或推送。即使存储库的初始克隆需要数分钟或数小时,通常最多可以在几秒钟内完成读取或推送(尽管这取决于您必须传输的数据量,两台计算机的速度以及它们之间的网络速度很快)。



在这个特定的例子中(你们都添加了提交128),这些新的提交与旧的

  o < - 您的第128次提交
/
...-- o- -o - o < - 127在这里连续提交
\
o < - 它们的第128次提交

提交流的这种分支是一种分支,并导致Git称之为分支,正如我所说我不打算进入这里。但实际上,您将您的 128次(或更高)提交与其第128次(或更高)提交和/或 merge base 提交,这是你在通过添加新的提交开始分散开来之前共同的最后一次提交。

公开与私有提交,或者有关制作所有这些功能正确



为了实现所有这些功能,Git依赖于无论发生在这些存储库克隆中的情况,您只需添加新的提交。



这是一个谎言:这里这个词取决于这个词太强大了。当你交叉连接两个Git仓库时,获得新提交的那个只是获得新的 。如果其他Git的用户已经抛出了一些提交,那么有两件事情可以保留:


  • 您已经提交了他们抛出了。你还有他们。您只是添加了 new 提交技术的独特性。

  • 你从来没有提交过的提交。你还没有他们。您只是将其余的剩余的新提交添加到您的技术显着性中。 应该清楚,或者至少清楚,但是:当您将Git连接到Git时,Git不会丢弃任何东西。你的Git只是添加新的东西



    对于 git push 操作:你的Git会给他们的Git new 提交,而不会带走一些。这是非常困难的(并且有些容易出错)。它可以通过Git所谓的强制推送来完成,但是这种机制有点牵扯,并且需要深入到Git的分支和其他Git调用的细节中。 EM>引用。因此,一般来说,您应该尽力只添加新的提交,或者至少只在您发布提交给其他Git时添加新的提交。



    当您首次将新提交添加到存储库时,这些新提交在 存储库中 。在这一点上,你可以放弃它们, 1 或者复制并替换它们, 2 或者其他任何东西,而且非常自由。但是,一旦你与另一个Git共享这些提交,那么其他Git就会提交这些提交,这些提交通过它们的神奇哈希ID来识别。现在很难收回它们。除非你有自己的服务器,并允许其他人 > from 你通常使用 git push 发布提交。所以如果你还没有推送一个提交 - 如果你只使用了 git fetch - 你可以自由地重写它。 (Mercurial使用一种叫做 commit phase 的方式跟踪提交是否已发布.Git不会,但Git的参考系统是Git调用的远程跟踪分支 ,这是一个可怕的短语 - 可以帮助你在这里,但是,这又一次进入了分支,我试图避免这种分支。)

    请注意,很容易撤消一个提交而不需要提交它:只需添加一个 new 提交,以反转前一提交的效果。 Git将此称为撤销; Mercurial称其为撤销。不过,无论哪种方式,您都要添加新的提交,这使得一切顺利。所以一旦你发布了提交,你可以恢复它们,并且没有任何复杂的每个人都必须同意撤消这个问题。唯一的缺点是,现在你有一个错误和纠正,永远出版,让世界看到。 : - )




    1 要放弃提交,可以使用 git reset - 虽然我会在这里注意到 git reset 是一个由三个不同部分组成的命令的科学怪人怪兽。它不一定丢弃提交:您可以使用它来丢弃未提交的工作,或者同时抛弃未提交的工作。事实上,你甚至可以用它来复活提交!



    2 替换 >一整条新链的提交链 - 链可以尽可能短,包括一个长期使用 git rebase 的提交。技术上来说,Rebase有点复杂;从 git merge 开始可能更明智一些,因为每个复制的提交在技术上都是由合并的。请注意,这些不可理解的散列ID 会在新副本中更改,因为新提交不同于原始提交。通过(通常)创建新的合并提交,合并避免了复制和更改ID的事情。


    I'm relatively new to git having come from a VSS / TFS / SVN background. I'm using the git plugin in Visual Studio 2015.

    So I clone a repository and make some changes to a code file. Then I look at the changes tab (which I presume does a git diff in the background) and I see the changes I made. All fine and dandy.

    Then I commit the changes to my local repo, after which I make some more changes to a local file, view the changes, and commit again.

    My problem is that there's now a blind spot between my local repo and the remote one - I've lost visibility of the differences between local and remote. This has been confusing me since I started working with git.

    Am I doing it all wrong... afaik "git diff" shows the changes between my local working code base and local repo. How do I easily see the difference between the changes that have been accumulating in my local repo and the code on the remote repository?

    解决方案

    You will want to diff commits. In particular, you will want to diff the current commit that is the tip-most commit that is in another repository against the tip-most commit in your own repository. But in the most general case, you cannot do that until you copy that commit—the other repository's commit—to your repository. This is because you are always working in your own repository, except for the brief periods when you connect your repository to a second Git with a second repository.

    This might be best illustrated visually (which requires showing a graphical representation of branching, which—at least in terms of physical implementation—is very different in Git than it is in SVN). Consider this drawing:

                 o--o--U   <-- commits you made
                /
    ...--o--o--B    [shared with another repository at the start]
                \
                 o--T   <-- commits they made when you weren't looking
    

    I gave letters to three of the "most interesting" commits (the others are just round o nodes). Newer commits are towards the right. The commits you have made are on the top line. Commits "they" made, that you have to bring in before you can do comparisons, are on the bottom line. Commits that both you and they had in common before you started all this are on the middle line.

    I believe you are asking how you compare the contents of U (for Us or oUrs) to the contents of T (for Them). The way to do that is with git diff plus the hash IDs of the two commits U and T. Note, however, that you can compare any pair of commits, such as B (for Base) vs U and B vs T, as well. When you merge your work with their work, Git will automatically compare B-vs-U and B-vs-T.

    To get the bottom row of commits you must run git fetch.

    If you are coming from SVN, many of these concepts themselves will be unfamiliar.

    Git vs SVN: a 30,000 foot overview

    I will expand a bit on what I said in comments, but still sticking to a high level view—one that, as much as possible, avoids mentioning branches, which SVN implements very differently. For a slightly deeper rapid introduction to Git branches, see https://stackoverflow.com/a/44081446/1256452 (an answer to need clarification on pulling git branches).

    One repository vs many repositories

    In SVN, there is only one actual repository. That repository, which holds every commit ever, is "far away" on some server somewhere, rather than on your computer. To work on something, you grab a bunch of code out of the repository. This puts the code on your own machine, where you can work on it. You do some work and when you are ready, you tell your machine:

    • Show me what I have, compared to what's on the server
    • Send what I have back to the server to make a commit

    Git usage is very different. There is not just one repository. There is one repository per user, or perhaps even more-than-one per user (you can make as many clones as you like, and each one is a repository in its own right). Your repository has every commit ever, and so does every other copy (every "clone"). Of course, each clone starts out the same as the original, but they do drift apart over time. How fast they drift apart depends on how active each clone is.

    Because separate repository clones exist, you need something SVN doesn't: a way to synchronize two repositories. In Git, these are called push and fetch. (You might think they should be push and pull, and in Mercurial, they are, but Git kind of over-defined pull initially and had to back up and introduce fetch. Don't use Git's pull right away, it mixes together two different ideas and will lead you astray.)

    Remember that whenever you are using these operations, there are two Gits involved, with two repositories. Each of these repositories is a complete repository in and of itself, with its own full set of commits, and ways to name those commits (Git calls these "branches" and "tags" and the purposes are similar, but these are very different from SVN's—you'll be best served by thinking, initially, about commits rather than "branches").

    When you first make your own clone, or do a git fetch to get your clone updated from its origin—which is cleverly named origin—what your Git does is rename their Git's commits. Since you have just cloned, or just run git fetch, your repository is now the same as, or a superset of, theirs. You now have everything they do, plus perhaps a bit more.

    Hence, when you want to see what you have that they don't—particularly, how your most recent commit compares to theirs—you simply do:

    git fetch origin    # get me their latest things
    

    and then:

    git diff <their-latest-commit> <my-latest-commit>
    

    to compare their highest revision to yours.

    Revision numbering: why Git hash IDs are weird

    There is a separate problem here, which I won't get into deeply, but will touch on. In SVN, "highest revision" is easy to tell, because there's only one repository and revisions are numbered sequentially. If your revision is number 128 and theirs is number 127, obviously yours is later than theirs, but if yours is 128 and theirs is 129, obviously theirs is later than yours. But here in Git, when you git fetch, you have two repositories. What if when you cloned, there were 127 commits, but both you and they have added one commit each? Now you both have 128 commits. You then cross-connect their Git to your Git and yank in their new commits, and now you have 129 commits and they have 128 commits. What happens?

    The first and most important thing that happens is that commits aren't numbered sequentially at all, so that your Git and their Git don't have to fight over who gets to call this "revision 128". Instead, Git commit IDs are big, ugly, incomprehensible hash IDs. The exact hash ID is something Git assigns when you make the commit. Git guarantees that your hash IDs for your new commits never collide with their hash IDs for their new commits.

    This is what allows you to cross-connect the two Gits in the first place: initially, your Git and their Git exchange just their latest hash IDs. These, plus algorithmic tricks using properties of Directed Acyclic Graphs, mean that your Git and their Git can quickly determine all the commits (and other Git objects) that need to be copied to complete a fetch or push. Even if the initial clone of a repository takes many minutes or hours, a fetch or push can usually complete in a few seconds at most (though this depends on the amount of data that you must transfer, how fast your two computers are, and how fast the network is between them).

    In this particular example (where you both added commit "128"), these new commits go "in parallel" when compared to older ones:

                 o   <-- your 128th commit
                /
    ...--o--o--o   <-- 127 commits in a row here
                \
                 o   <-- their 128th commit
    

    This forking of commit streams is a kind of branch and leads to what Git calls "branches", which as I said I am not going to get into here. But essentially, you compare your 128th (or higher) commit to their 128th (or higher) commit, and/or to the merge base commit, which is the last commit that you both had in common before you started to drift apart by adding new commits.

    Public vs private commits, or, a few words about making all this function correctly

    To make all this work, Git depends on the idea that whatever happens with these repository clones, you only add new commits.

    This is sort of a lie: the word depends here is too strong. When you cross-connnect two Git repositories, the one that acquires new commits just gets the new ones. If the user of the other Git has thrown away some commits, then one of two things holds:

    • You already had the commits they threw out. You still have them. You have merely added new commits to your technological distinctiveness.

    • You never had the commits they threw out. You still don't have them. You have merely added their remaining new commits to your technological distinctiveness.

    The principle here should be clear, or at least clear-ish, though: your Git is not throwing anything away when you connect your Git to their Git. Your Git is only adding new things.

    The same normally goes for a git push operation: your Git will give their Git new commits, not take some away. It's very difficult (and somewhat error-prone) to take things away. It can be done, via what Git calls a force push, but the mechanism is somewhat involved and requires getting down into the details of Git's branches and other of what Git calls references. So in general, you should endeavor to only add new commits—or at least, only add new commits once you publish a commit by giving it to another Git.

    When you first add new commits to your repository, these new commits are only in your repository. At this point, you can discard them,1 or copy-and-replace them,2 or whatever, with great freedom. But once you share those commits with another Git, that other Git has those commits, which it knows by their magic hash IDs. It's now very difficult to retract them.

    Unless you have your own server and allow others to git fetch from you, in general, you publish commits using git push. So if you have not yet pushed a commit—if you have only used git fetch—you are free to rework it. (Mercurial keeps track of whether a commit has been published, using something called a commit phase. Git doesn't, but Git's reference system—what Git calls remote-tracking branches, which is a terrible phrase—can help you out here. But that, again, gets into branches, which I am trying to avoid.)

    Note that it's easy to undo a commit without taking it back: just add a new commit that reverses the effect of the previous commit. Git calls this a revert; Mercurial calls it a backout. Either way, though, you're adding new commits, which makes everything work smoothly. So once you have published commits, you can revert them, and there are no complicated "everyone must agree to retract" issues with that. The only drawback is that now you have a mistake and its correction, published forever for the world to see. :-)


    1To discard commits, you can use git reset—though I will note here that git reset is a bit of a Frankenstein monster of a command made of three different parts. It doesn't necessarily discard commits: you can use it to discard uncommitted work instead, or at the same time. In fact, you can even use it to resurrect commits!

    2To replace an entire chain of commits with an entire new chain—the "chain" can be as short as you like, including just one commit long—use git rebase. Rebase is a bit complicated technically; it's probably wiser to start with git merge, since each copied commit is technically made by merging. Note that those incomprehensible hash IDs change in the new copies, since the new commits are different from the originals. Merging avoids the copy-and-change-ID thing, by (normally) creating a new merge commit.

    这篇关于分级和远程之间的Git盲点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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