“下游”和“上游”的定义是什么?它们与IT技术有什么关系?

1053

我开始使用Git,并遇到了“upstream”和“downstream”这些术语。我以前见过这些词,但从未完全理解它们。在SCM(软件配置管理工具)和源代码方面,这些术语是什么意思呢?


20
在 Git 中,“upstream/downstream” 有两个不同的上下文:远程仓库和时间/历史。关于远程仓库的上下游,下游仓库将从上游仓库拉取(变更自然向下游流动)。关于时间/历史的上下游可能会让人感到困惑,因为时间上的上游意味着历史上的下游,反之亦然(家谱术语在此非常适用——父级/祖先/子级/后代)。 - charlesreid1
8
相关:在OS上,“upstream”是什么意思?(http://opensource.stackexchange.com/q/993/407) - kenorb
6
相关内容:在GitHub上有一个关于“源和上游”的区别的问题。 - RBT
6个回答

831

在源代码控制方面,当你从仓库中复制(克隆、检出等)时,你处于下游。信息向你“下游”流动。

当你进行更改时,通常希望将其发送回“上游”,以便它们进入仓库,这样每个人从相同的源码中拉取都会使用相同的更改。这主要是一个社交问题,涉及如何协调工作,而不是源代码控制的技术要求。你需要将自己的更改合并到主项目中,这样就不需要跟踪分支发展路线。

有时候,你会看到软件包或发行版管理者(指人员,而非工具)谈论将更改提交给“上游”。这通常意味着他们必须调整原始代码,以便为其系统创建软件包。他们不想继续进行这些更改,因此如果将它们发送给原始来源,则在下一个版本中就不必处理相同的问题。


153
“Download”和“upload”是动词。“Upstream”和“downstream”描述了相对位置。 - brian d foy
4
我会说"upstream"和"downstream"是形容词。 - kgui
16
当这些词被用作修饰语时,它们是形容词,但这些术语常常被用作名词。 - brian d foy
4
根据上下文,单词可以被用作形容词或名词。 - reggaeguitar
1
事实上,“上游”和“下游”不仅可以用作形容词(如“上游存储库”)或名词(如“我不关心下游”),还可以用作副词(如“那个存储库在我的上游”或“你需要的一切已经在下游了”)。 - Alan
显示剩余3条评论

287
当你阅读 git tag man page 时,会看到以下内容:

Git 的一个重要特点是它是分布式的,而且作为分布式系统,在系统中不存在固有的“上游”或“下游”。

这意味着简单来说,并没有绝对的上游或下游仓库存在。这些概念总是相对于两个仓库并取决于数据流的方式:
如果 "yourRepo" 声明了 "otherRepo" 作为远程仓库,则:
  • 你正在从 "upstream" "otherRepo" 拉取代码("otherRepo" 对于你来说是"上游仓库",你是"下游仓库")。
  • 你正在向 "upstream" 推送代码(此时 "otherRepo" 仍然是 "上游仓库",信息返回到那里)。
请注意 "from" 和 "for": 你不仅仅是"下游仓库",还可以形容为"相对于/为"下游仓库,这就是相对性的表现。
DVCS(分布式版本控制系统)的奥妙在于:你其实不知道 "downstream" 到底是什么,只知道自己的仓库与声明的远程仓库相对关系。
  • 你知道 "upstream" 是什么(你正在拉取或推送的仓库)
  • 你不知道 "downstream" 是由什么组成的(其他仓库正在从你这里拉取或推送)。
基本上,就是:

在“数据流”的术语中,你的仓库处于底部(“下游”),来自上游仓库的流向它(“拉取”),然后再返回(相同或其他)上游仓库(“推送”)。


您可以在git-rebase手册页面中看到一个示例,其中包含“RECOVERING FROM UPSTREAM REBASE”段落: 这意味着您正在从发生rebase的“upstream” repo中拉取,并且您(即“downstream” repo)被卡在了后果中(因为分支在“upstream”重新创建了您本地具有相同分支的提交而导致了许多重复提交)。
这很糟糕,因为对于一个“upstream” repo,可能会有许多 downstream repo(即从upstream repo拉取、带有rebased分支的repo),它们都有可能需要处理重复提交。
再次强调,在DVCS中,“数据流”类比下,一个错误的“upstream”命令可能会在“downstream”产生“连锁反应”。
请注意:这不仅限于数据。
它也适用于参数,因为Git命令(如“porcelain”命令)常常在内部调用其他Git命令(“plumbing”命令)。详见rev-parse手册页面:

许多Git porcelainish命令使用混合标志(即以破折号“-”开头的参数)和参数,这些参数用于底层的git rev-list命令,它们在内部使用并且包括用于其他downstream命令的标志和参数。此命令用于区分它们。


19
您说的意思是从上游拉取(pull from upstream),向上游推送(push to upstream)。对下游进行推送(push to downstream)听起来非常不对。 - knittl
3
@knittl:你说得对。我已经重新表述了我的答案,以更好地阐述“上游”库与您自己的本地(和“下游”)库之间的角色关系。 - VonC

91

上游跟踪

术语上游在GIT工具套件中也有一些明确的含义,特别是相对于跟踪而言。

例如:

   $git rev-list --count --left-right "@{upstream}"...HEAD
   >4   12

will print (the last cached value of) the number of commits behind (left) and ahead (right) of your current working branch, relative to the (if any) currently tracking remote branch for this local branch. It will print an error message otherwise:

    >error: No upstream branch found for ''
  • 正如已经提到的那样,您可以为一个本地仓库拥有任意数量的远程仓库。例如,如果您从 Github fork 了一个仓库,然后发起了一个“pull request”,那么您肯定至少有两个远程仓库:origin(在 Github 上的您 fork 的仓库)和 upstream(您从中 fork 的 Github 上的仓库)。这些只是可互换的名称,只有 'git@...' url 才能识别它们。

Your .git/configreads :

   [remote "origin"]
       fetch = +refs/heads/*:refs/remotes/origin/*
       url = git@github.com:myusername/reponame.git
   [remote "upstream"]
       fetch = +refs/heads/*:refs/remotes/upstream/*
       url = git@github.com:authorname/reponame.git
  • 另一方面,@{upstream} 在GIT中的意义是独特的:

它是在'所述远程'上跟踪'本地仓库''当前分支''分支'(如果有的话)。

这是您在没有参数的情况下发出纯git fetch/git pull时从中获取/拉取的分支。

假设您想将远程分支origin/master设置为您已检出的本地master分支的跟踪分支。只需执行以下操作:

   $ git branch --set-upstream  master origin/master
   > Branch master set up to track remote branch master from origin.

This adds 2 parameters in .git/config :

   [branch "master"]
       remote = origin
       merge = refs/heads/master

now try (provided 'upstream' remote has a 'dev' branch)

   $ git branch --set-upstream  master upstream/dev
   > Branch master set up to track remote branch dev from upstream.

.git/config now reads:

   [branch "master"]
       remote = upstream
       merge = refs/heads/dev

git-push(1) Manual Page :

   -u
   --set-upstream

For every branch that is up to date or successfully pushed, add upstream (tracking) reference, used by argument-less git-pull(1) and other commands. For more information, see branch.<name>.merge in git-config(1).

git-config(1) Manual Page :

   branch.<name>.merge

Defines, together with branch.<name>.remote, the upstream branch for the given branch. It tells git fetch/git pull/git rebase which branch to merge and can also affect git push (see push.default). \ (...)

   branch.<name>.remote

When in branch < name >, it tells git fetch and git push which remote to fetch from/push to. It defaults to origin if no remote is configured. origin is also used if you are not on any branch.

上游和推送(陷阱)

请查看git-config(1)手册页

   git config --global push.default upstream
   git config --global push.default tracking  (deprecated)

This is to prevent accidental pushes to branches which you’re not ready to push yet.


5
2018年的《git branch --help》摘录:由于该选项语法混乱,因此不再支持。请改用--track或--set-upstream-to。 - zezollo

65

这是一些非正式的术语。

就 Git 而言,每个其他的代码库都只是一个远程仓库。

通常而言,上游是你从哪里克隆的(即源头)。下游是任何一个将你的工作与其他工作集成起来的项目。

这些术语并不局限于 Git 代码库。

例如,Ubuntu 是 Debian 的一个衍生版,因此 Debian 对于 Ubuntu 来说就是上游。


59

Upstream不好的用法

很遗憾,这里的其他答案并没有涉及到“upstream”另一种用法,即指代仓库内提交记录的父子关系。Scott Chacon在 Pro Git 书籍 中经常使用此用法,结果是不幸的。不要模仿这种说话方式。

例如,他说合并导致快进时会发生这种情况,是因为:

你要合并的分支所指向的提交与你当前的提交直接上游

他想表达的意思是提交B是提交A的唯一子代的唯一子代的...的唯一子代,所以将B合并到A中只需移动A引用指向提交B即可。为什么应该将此方向称为“上游”,而不是“下游”,或者为什么这样纯粹的直线图形的几何应该被称为“直接上游”,完全不清楚,可能是任意的。(当说“当前分支头是指定提交的祖先”时,git-merge手册做得更好。这就是Chacon应该说的内容。)

事实上,当他谈到重写删除提交后所有子提交时,Chacon似乎在后面使用“下游”来完全指代同样的意思:

  

您必须重写从6df76开始的所有下游提交,以完全从Git历史记录中删除此文件

基本上,他似乎没有任何清晰的想法,关于引用提交历史的时间轴时“上游”和“下游”的含义。这种用法是非正式的,不应鼓励,因为它只会造成混乱。

很明显,每一个提交(除了一个)都至少有一个父节点,而父节点的父节点因此成为祖先;反过来,提交也有子节点和后代。这是公认的术语,并清楚地描述了图形内各个提交之间的关系方向,因此在描绘代码库中提交之间相互关联时,应该使用这种方式。不要在这种情况下松散使用"上游(upstream)"或"下游(downstream)"。

[额外说明:我一直在思考Chacon所引用的第一句话与git-merge手册之间的关系,我发现前者可能基于对后者的误解。手册确实描述了一种情况,在这种情况下使用"上游(upstream)"是合理的:当"您正在跟踪上游存储库,没有提交本地更改,现在想要更新到较新的上游修订版本"的时候,通常会发生快速前进。所以,也许Chacon之所以使用"upstream"是因为他在这里看到了它。但在手册中,有一个远程存储库;在Chacon引用的快速前进示例中不存在远程存储库,只有几个本地创建的分支。]


15
git-rebase的手册也存在这种过载问题:在变基之前检出的提交称为“上游”。这也可能影响了Chacon的用法。 - outis
@outis 奇怪 - 在 git 的 HTML 文档中,rebase 之前检出的分支被称为 <branch> - Jesper Matthiesen
1
好的观点。在某个地方收集常见的“git术语”可能会有所帮助,特别是对于新手(或参与git贡献的人)。这将为我节省很多时间来适应git手册中的措辞。 - clickMe
@SebNag 像这样吗?https://linuxacademy.com/blog/linux/git-terms-explained/ - reggaeguitar
1
因为我完全不理解提交引用会在git-rebase文档中被称为“upstream”,所以我从那里来到这里(事实上,我还怀疑自己是否之前看过这个术语)。感谢@outis和@matt澄清了事情! - Borek Bernard
git-scm文档/参考书对于理解本来就存在问题的设计决策(例如重载方法、不一致的语法)是积极有害的。 - mooncoder

8
使用河流的比喻,我们可以从当前资源向上游追踪,直到找到河流或小溪的源头。
继续使用河流的比喻,下游是河流水流的方向。朝着下坡方向。
所以,如果我fork(派生)了某人的项目,则我派生的项目相对于该项目在上游。而我的 fork 则是下游。
如果有人 fork 我的派生项目,则相对于我的派生项目,我的 fork 就成为了上游项目。
我的 fork 的 fork 就成为了下游项目。
举个例子吧! 假设Project Bfork了Project A,并且Project C又fork了Project B
那么,Project A是上游项目。 Project B是相对于Project A的下游项目。 Project B是相对于Project C的上游项目。 Project C是相对于Project B的下游项目。
这个循环不断地进行。
注意:请注意,这是开源项目中一种常见的开发样式,即创建一个项目的 fork,在该 fork 中修复错误或添加功能,然后将补丁提交到原始项目中。
还要注意,“质量运动”和统计过程控制中的一个明确教训是,解决质量问题的干预措施几乎总是比反复修复可预防的问题更好的投资。所以请贡献补丁(发送Pull requests)。

1
讲解得非常清楚! - projektorius96

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接