2014-12-30 94 views
2

我最近开始使用SourceTree和Git,但我仍然对存储/分支和工作副本文件感到困惑。我一直无法找到任何明确的东西给我。隐藏/分支/工作副本混淆

我的困惑是关于文件的工作副本关于分支和窗口。我期望的事情的工作方式是,我的文件的工作副本也将链接到分支。如果我要切换分支,它会在切换到新分支之前自动保存我的工作副本。如果我要切换回前一个分支,它将再次存储当前的工作副本文件并从前一个分支中恢复这些文件。

在使用SourceTree和Git一段时间之后,它对我来说显得非常清楚,它不是如何工作的,而且工作副本文件与您的分支完全无关,就像存储。如果切换到另一个分支,您的选择是手动存储工作副本文件,放弃更改或将您的工作文件带到新分支。

所以,我想知道的是,这是什么理想的工作流程?假设我正在两个不同的分支中同时开发两个功能,并且想不断地来回跳动。我是否需要记住在每次切换前存储我的工作副本文件或者是否有更好的方法?

+0

*如果我要切换分支,它会在切换到新分支之前自动保存我的工作副本。如果您有未提交的更改与您正在检查的分支发生冲突,那么Git不会让您这样做。你需要“手动”存储它们; Git不会自动为你做。 – Jubobs

+0

好吧,知道我不只是错过了一些东西。我猜想SourceTree中的弹出窗口询问你是否想要放弃本地更改也应该提醒您在切换之前隐藏它们。 – Mathieson

+0

如果你从命令行使用Git,我相信你会有更好的体验,但这只是我的看法。特别是,我不确定SourceTree是否允许您定义别名/宏。 – Jubobs

回答

1

如果我要切换分支,它会自动存储我的工作副本,然后切换到新分支。

不! Git不会自动为您保存本地更改。另外,如果您有未提交的更改与您正在检查的分支发生冲突,那么Git不会让您检出相关分支。您需要在检出其他分支之前“手动”丢弃或隐藏它们。

它似乎很清楚我的工作拷贝文件是完全独立于你的分支,就像是存储。

是的,这有很好的理由。特别是,一个用于存储的用例是在检出“错误”分支时开始进行更改。然后,您可以通过

  • 积攒你的本地修改(从而降落在一个干净的工作状态)走出困境,
  • 检查出“正确”的分支,
  • 大跌眼镜的是藏匿恢复您的本地更改。

说我是同时开发两种功能,在两个不同的分支,并希望不断来回跳转。我是否需要记住在每次切换前存储我的工作副本文件或者是否有更好的方法?

是的,在切换到另一个分支之前存储您的更改,然后弹出以前的存储,将是正常的工作流程。如果您经常切换分支,那确实很乏味,但是如果您使用命令行中的Git,则可以定义aliases以抽取某些复杂性。

我从来没有使用过SourceTree,但我可以想象如何存储/检出/弹出可能涉及很多繁琐的鼠标点击。不过,显然,SourceTree 引入了一种名为“Custom Actions”的机制,允许您定义自己的命令,including Git commands。你可能想看看它......

2

“TL; DR”部分:提防只是盲目做git stash save && git checkout ... && git stash pop


实际上有几件你可以取笑,特别是在使用git的命令行界面时。

特别是,“当前分支”(如果有的话)只是记录在文件中的项目(该文件包含HEAD参考,.git/HEAD)。查看该文件的原始内容,您通常会看到ref: refs/heads/master等。 (在“分离HEAD”模式下,您将看到一个原始SHA-1。)有一些低级别的git命令会更新HEAD而不做任何其他操作。

但是,大多数人主要使用git checkout来切换分支,除了正确的方法:-)来做到这一点 - 内置了一些保护。主要是,如果您有工作树,它将拒绝切换分支“索引”(AKA缓存)修改将被这样的交换机丢失。假设您在分支A上,并且您要求切换到分支B。结帐过程必须:

  • 得到存在于尖端的所有文件的清单提交分行A
  • 的获取存在于尖端的所有文件的列表选项B
  • 的承诺在文件第一个不在第二个的列表中,从第二个列表中不在第一个文件的工作目录
  • 中删除这些文件,将这些文件添加到工作目录中
  • 中的文件如果(且仅当)文件不同,则替换工作目录内容[R

此外,退房时被做缓存“通过书写”:如果文件FAB不同,的B版本的内容将首先被复制到索引/缓存,然后写入工作目录。

如果你已经上演(与git add)一些修改文件F,或者你有一些未上演修改F在你的工作目录,该结账过程将覆盖这些阶段性或非阶段性变化,因此git checkout停止带有错误信息。如果文件F将被删除,那么也会覆盖(或者更准确地说,删除)您的更改,因此checkout也会停止。

在另一方面,如果文件F两个犯了同一,在checkout可以继续:它只是离开未提交的更改上演或不分级。这就是为什么你有时可以,但不总是,只需git checkout你想要工作的分支。


Git的 “藏匿”(如git stash),正如你所看到的,独立的分支。这里的关键概念是每个存储 - 你可以有多个活动 - 实际上是一个提交(或更确切地说,一组提交:两个或三个,这取决于你存储的内容)。但是,在您反对在分支机构上进行提交之前,我们必须再做一些区分。具体来说,“分支”一词指的是两三个不同的东西,在git中。

提交总是(必然)进入“提交图”,因为图仅仅是由所有提交和它们的边形成的东西。如果“分支”一词意味着“提交图的一部分”,这些存储提交在分支上。但“分支”这个词也指的是标识分支的名称提示提交,并且在这里,这些提交提交不会推进分支提示。 (见this post,也Jubobs,对的“分支”的多重含义的更多细节。)

git stash命令查看当前索引/缓存和工作树的状态,并从他们身上,使新的提交如果 - 这个“如果”事实证明是非常重要的 - 如果他们有未保存的分阶段或未分阶段的变化。其中一个新提交(可从其中找到其他提交)保存在特殊参考名称stash下。尽管没有任何新的提交被添加到当前分支,所以它们不在任何命名的分支上。从这个意义上说,它们独立于当前分支 - 但是因为它们是提交,所以它们具有父提交ID,并且从特定意义上讲,它们直接附加到完成时提交的提交。

这对于最终用户来说意味着什么往往是“没有”:你可能不在乎,也不必关心。但是,如果您曾使用git stash branch将存储转换为分支,则意味着新分支将从存储所附的提交中分离出来。 (这一点,因为它的出现,通常是你想要什么。)与定义,做git stash save && git checkout ... && git stash pop别名或宏


一个风险是第一步,git stash save,可以什么都不做。如果它什么都不做 - 如果它不在“存储堆栈”上推送新的存储 - 然后它成功了,并且你的别名或宏将继续检出其他分支,然后(尝试)从藏匿处堆积出来。

如果在该堆栈上还有另一个(不同的)存储器,那么你打算在其他地方使用,那么你只是试图将它弹出到你刚切换到的分支中。

注意,有两个堆叠的“如果”在这儿,必须持有,这个特定的错误两个条件咬:

  • 你需要什么都没有藏匿,并
  • 你需要有一些现存的你不要想要弹出。

解决此问题的一种方法是使用脚本,而不仅仅是一个简单的git别名来执行分支交换存储序列。在脚本中,像往常一样运行git stash,但在存储之前和之后,检查stash参考所解析的SHA-1(如果有)。如果这个变化,git stash save保存了一些东西,所以有一些东西可以弹出,你可以继续做结账和流行的顺序。如果它没有改变 - 如果前后没有隐藏,或者如果堆栈顶部的存储仍然在堆栈的顶部,那么没有任何可以弹出的东西,你应该只是做一个结帐。

还有另一个bug可以咬你这里;看到this answer到一个有点不同的问题,其中包括一些shell代码表示上述“只有当save实际推动某些东西”的规则弹出。

+0

一如既往,阅读起来很不错,但是你在可怜的OP(只通过一个GUI知道Git)的时候就会抛弃它!关于'git stash save'如何可以咬你的好点儿。我没有意识到这一点。 – Jubobs

+1

这就是为什么我在顶部添加了“TL; DR”位:-) – torek