简体   繁体   English

git:如何将更改添加并提交到一个远程分支,而不提交到另一个?

[英]git: how to add and commit changes to one remote branch but not another?

So I know the way to push to a specific remote is: 所以我知道推送到特定遥控器的方法是:

git push <remote branch> master where master is my local branch git push <remote branch> master ,其中master是我的本地分支

I made some sensitive changes locally that I would like to push to remote A but not remote B. But to push to remote A, I need to add and commit my changes on my local branch before I can push. 我在本地做了一些敏感的更改,我想将其推送到远程A而不是远程B。但是要推送到远程A,我需要在本地分支上添加并提交更改,然后再进行推送。 However, after I do that, won't the commit be in my local branch history? 但是,执行完此操作后,提交内容将不在我的本地分支历史记录中吗? Do I have to manually remove that commit before future pushes to remote B or is there a more established way to do this? 在将来推送到远程B之前,我是否必须手动删除该提交,或者是否有更成熟的方法来做到这一点? (Because then every time I work on remote AI have to remember to make the change again, vis versa for B) (因为那样,每次我使用远程AI时都必须记住再次进行更改,反之亦然)

Some of this depends on just how much trust you want to give to Git, because in the bad old days (git 1.5 or 1.6 or so) I have seen Git send objects to a remote that it should not have sent. 其中一些取决于您要给予Git多少信任,因为在糟糕的过去(git 1.5或1.6左右),我已经看到Git将对象发送到它不应该发送的远程对象。 So I, at least, would not be this trusting—but this is how it is supposed to work. 因此,至少我不会获得这种信任-但这就是它应该如何工作的方式。

We need some definitions, or we will get tripped up by some not-so-great naming in Git. 我们需要一些定义,否则我们将在Git中被一些不太好的命名绊倒。 They are below (towards the end) so that you can skip them if you are already familiar with them. 它们位于下方(即将结束),因此如果您已经熟悉它们,可以跳过它们。

The bottom line is that when you do the push , your git will send, to the remote, your branch tip commit and all commits reachable from that point (ie, all of its ancestors) up to whatever commits the remote already has. 最重要的是,当您执行push ,您的git会将分支提示提交以及从该点 (即,其所有祖先)到远程已经拥有的所有提交都可到达的所有提交发送到远程。 It will also include all files needed for those commits. 它还将包括那些提交所需的所有文件。 It should send only those commits and files, ie, if some sensitive file or data is not in those commits, and the remote does not have it already, the remote should not have it after the push. 应该 发送那些提交和文件,即,如果某些敏感的文件或数据不在那些提交中,并且远程还没有它,则在推送之后远程不应该拥有它。

Ultimately, this probably means that you should keep two different sets of branches for the two remotes. 最终,这可能意味着您应该为两个遥控器保留两组不同的分支。 Or—better— don't put any sensitive data into Git in the first place. 或者-更好-首先不要将任何敏感数据放入Git。 Keep it somewhere outside the repository. 将其保存在存储库之外的某个位置。 If you need a configuration file that may contain such data, include a sensitive-data-free sample configuration in the repository, and use .gitignore to avoid putting the real configuration into the repository. 如果您需要一个可能包含此类数据的配置文件,请在存储库中包含无敏感数据的示例配置,并使用.gitignore 避免将实际配置放入存储库中。

Discussion 讨论区

Remember that a branch name "points to" a particular commit (by containing its ID): 请记住,分支名称“指向”特定提交(通过包含其ID):

$ git rev-parse master
3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243

and that commits point to their parent commit(s) (by containing their IDs), as well as to trees that point to blobs that get you the files that go with those commits. 并且这些提交指向其父提交(通过包含其ID),以及指向Blob的树,这些Blob为您提供了与这些提交一起使用的文件。 To work with commits, Git needs some starting-point, such as this SHA-1 hash 3ad15fd... . 为了处理提交,Git需要一些起点,例如SHA-1哈希3ad15fd... It will work backwards from there, following all of these pointers, in order to check out any particular commit. 它将跟随所有这些指针从那里向后工作,以检出任何特定的提交。 Each ID (of a commit, tree, or file) gives Git the "true name" of the underlying object, and Git uses this true name to extract the actual contents. 每个(提交,树或文件的)ID为Git提供基础对象的“真实名称”,Git使用此真实名称提取实际内容。

Hence, if there is sensitive data in some file, the way Git stores it is under some blob ID. 因此,如果某个文件中包含敏感数据,则Git的存储方式将位于某个Blob ID下。 The way you see it through Git is by starting Git with the ID of a commit, which Git uses to find the tree and blob IDs. 通过Git看到它的方式是通过以提交ID开头Git,Git用来查找树和Blob ID。 Git then extracts the blob object, using the name specified by the tree(s), so that you now have a file with the right name and with those sensitive data contents. 然后,Git使用树指定的名称提取blob对象,这样您现在就拥有了一个具有正确名称和敏感数据内容的文件。

If Git does not have the blob object at all, then clearly, you cannot get it out of Git. 如果Git根本没有blob对象,那么显然,您无法将其从Git中删除。 If it has the blob object, but no commits point (through trees) to it, you cannot get it by file name—but you can , with some maintenance commands, have Git show you every blob's raw ID and extract them all by ID and thus find that data. 如果它具有blob对象,但没有提交指向(通过树)的对象,则无法通过文件名获取它-但可以通过一些维护命令让Git为您显示每个blob的原始ID,并按ID和因此找到该数据。 (Usually it's even easier than that, just git fsck --lost-found .) (通常比这更容易,只需git fsck --lost-found 。)

This means that to ensure the sensitive data are not in some Git repository, you must make sure that the blob itself is not there. 这意味着要确保敏感数据不在某个Git存储库中,必须确保blob本身不存在。 This also means that any commits that refer to the blob must not be there, as Git won't allow you to have a commit that has "missing" blobs. 这也意味着任何引用blob的提交都不能存在,因为Git不允许您进行包含“缺失” blob的提交。 (Such a repository is considered broken: you get errors trying to use it, though Git will do its best to let you recover whatever data you can.) (这样的存储库被认为是损坏的:尽管使用Git会尽力使您恢复所有可能的数据,但是尝试使用它会出错。)

git push syntax and semantics git push语法和语义

The syntax for git push , simplified to only what we care about here, is: git push的语法简化为仅在这里我们关心的是:

git push remote refspec

When you run this, git starts by connecting to the named remote according to its url setting (actually, its pushurl if set, falling back to url if not). 运行此命令时,git首先根据其url设置(实际上是其pushurl如果已设置),然后回退到url )连接到命名的remote The remote, usually some far-away host such as github, runs a command, typically git receive-pack , which then reads requests from your own git push . 远程(通常是一些遥远的主机)(例如github)运行命令,通常是git receive-pack ,然后从您自己的git push读取请求。

Your git push starts by sending the IDs of commit objects, essentially offering those objects to the receive-pack running on the remote. git push从发送提交对象的ID开始,本质上是将这些对象提供给在远程上运行的receive-pack The remote replies with either "I have that already" or "ok, I'll take that, send it to me." 远程用户回答“我已经拥有”或“好,我会接受它,然后发送给我”。 It is your Git's job to offer only those objects that are needed to complete the push. Git的工作是仅提供完成推送所需的那些对象。 Your Git should start by offering the commit ID found by parsing the src part of the refspec you supplied, and then offer trees and blobs needed to complete that commit, and ancestors of that commit, until the points where the remote says "I have that one already". 您的Git 应该首先提供通过解析您提供的refspec的src部分找到的提交ID,然后提供完成该提交所需的树和Blob,以及该提交的祖先,直到遥控器说“我拥有那个一个”。 This is how your Git knows what to send, and is able to send only whatever is not already on the remote. 这就是您的Git知道要发送什么内容的方式,并且只能发送遥控器上尚未发送的内容。 1 1个

Your Git then packages those objects and sends the package. 然后,您的Git打包这些对象并发送该包。 This should contain only those objects that were offered and accepted. 包含提供和接受的那些对象。 These may be further compressed against objects that your Git knows their Git has based on its claims that it already has them. 根据您的Git声称自己已经拥有Git的对象,这些对象可能会进一步针对您的Git知道的对象进行压缩。

(This is where, in the bad old days, things seemed to not work as desired, because my sending machine would send unnecessary, and in fact unreachable, objects to the remote. This is not supposed to happen. I'm not sure if it was a failure in the offer/accept phase or in the pack-building phase, or if it was caused by other rather unorthodox stuff I was doing at the time.) (在过去的糟糕日子里,这似乎无法按预期运行,因为我的发送计算机会将不必要的,实际上是无法到达的对象发送到远程。这不应该发生。我不确定是否这是在要约/接受阶段或包装制作阶段的失败,或者是由于我当时在做的其他非常非常规的事情造成的。)

Finally, your Git sends their Git a request that amounts to "now please set your reference ref to a particular hash", where ref is the name from the dst part of the refspec, and the hash is the ID your Git found by parsing the src part of the refspec. 最后,您的Git向其Git发送一个请求,该请求等于“现在请将您的引用ref设置为特定的哈希”,其中ref是refspec的dst部分中的名称,而哈希是您的Git通过解析src部分。 Their Git can either decide to allow this, or to reject the request, based on whatever rules they set up. 他们的Git可以根据他们设置的任何规则决定允许这样做,还是拒绝请求。 (The default rule is to allow it for branches if and only if it is a fast-forward, or a new branch creation. I'm also glossing over deletions here.) (默认规则是当且仅当它是快速转发或新创建分支时才允许它使用分支。在此我也掩盖了删除内容。)

There is a bit of magic involved to turn a short-name dst name into a full-name reference: git checks your src to see if it is a branch or tag, and expands the dst to start with refs/heads/ or refs/tags/ as needed. 将短名称dst名称转换为全名引用涉及一些魔术:git检查您的src以查看它是否是分支或标记,并扩展dstrefs/heads/refs/tags/开头refs/tags/根据需要。 If you give a full-name dst , your Git skips this step. 如果输入全名dst ,则Git跳过此步骤。 If you omit the : dst part of the refspec entirely, your Git constructs the full branch or tag name for their Git according to some rather complicated rules. 如果您完全省略了: dst: dst部分,则您的Git会根据一些相当复杂的规则为其Git构造完整的分支或标记名称。 For branches, usually the result is just the same full name as for your own branch, though. 对于分支机构,结果通常与您自己的分支机构的全名相同。

In other words, if you do: 换句话说,如果您这样做:

git push remote1 mybranch:theirbranch

then your Git will call up the Git for remote1 over the Internet-phone (assuming a remote URL), package up whatever they need to get all objects (commits, trees, and files/blobs) they do not already have that they would need for your mybranch , and ask them to make their branch theirbranch point to that commit. 那么你的Git会调出的Git为remote1通过互联网电话(假定远程URL),包了一切他们需要得到所有对象(犯,树木和文件/斑点),他们已经没有,他们将需要您mybranch ,并要求他们,使他们的分支theirbranch指向该承诺。

If you then do: 如果您这样做:

git push remote2 differentbranch:theirbranch

your Git will call up the Git on remote2 and send it whatever objects it needs to match up with your differentbranch , and ask them to set their branch theirbranch to point to the ID that differentbranch names. 你的Git会调出的Git上remote2和发送它需要与你匹配任何对象differentbranch ,并要求他们建立自己的分支theirbranch指向该ID differentbranch名。

You can also, in this particular case (this hash is from the repository for git itself), do: 您也可以在这种特殊情况下(此哈希来自git本身的存储库),可以执行以下操作:

git push remote3 3ad15fd:refs/heads/branch

Note that this time, you have specified a raw commit ID, so you must spell out the full name of the branch for the remote. 请注意,这一次,您指定了原始提交ID,因此您必须拼写出远程分支的全名。 As before, this will call up the remote, converse with it to see if it already has 3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243 , send it that commit if needed, also sending it any ancestor commits, trees, and blobs needed. 和以前一样,它将调用远程对象,与之进行交谈,以查看它是否已经具有3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243 ,如果需要将其发送给提交,还向其发送任何所需的祖先提交,树和Blob。 Finally it will send a request for their Git to set their branch to 3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243 . 最后,它将发送请求,要求其Git将其branch设置为3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243 If they accept, they now have that commit and all its ancestors (including each merged-in commit and its ancestors), along with all the trees and blobs for all those commits. 如果他们接受了,他们现在将拥有该提交及其所有祖先(包括每个合并的提交及其祖先),以及所有这些提交的所有树和Blob。


1 This skips over an important optimization: the remote actually starts by listing SHA-1s and references it already has, which lets your Git not even bother offering those. 1这跳过了一个重要的优化:远程实际上是从列出SHA-1并引用它已经拥有的开始,这使您的Git甚至不必费心提供这些。

Definition: remote 定义:远程

A remote is simply a name, like origin or upstream , under which you store various entries in your local repository's configuration. 远程只是一个名称,例如originupstream ,您可以在其下将各种条目存储在本地存储库的配置中。 The one that you must set (usually, initially, implicitly by cloning) is the url , something generally of the form git://... , http://... , or ssh://... . 您必须设置的url (通常最初是通过克隆来隐式设置)是url ,通常采用git://...http://...ssh://... Git stores several other configuration entries here as well though, including one or more fetch = entries. 不过,Git还在此处存储了其他几个配置条目,包括一个或多个fetch =条目。 Use git config --edit to view your configuration in your editor (be careful not to modify it), or just run less .git/config or similar to view it, and you will see things like: 使用git config --edit在编辑器中查看您的配置(请注意不要对其进行修改),或者只运行less .git/config或类似内容即可查看它,您将看到类似以下内容:

[remote "origin"]
    url = git://some.host.somewhere.com/path/to/repo.git
    fetch = +refs/heads/*:refs/remotes/origin/*

Again, the remote is just the name, in this case origin . 同样, 遥控器只是名称,在本例中为origin

Definition: branch 定义:分支

The word "branch" in Git is ambiguous . Git中的“分支” 一词含混不清 The meaning I use here is that of the moveable pointer, and more specifically the branch name , such as master or develop . 我在这里使用的含义是可移动指针的含义,更具体地说是分支名称 ,例如masterdevelop This name has a "full name" variant, so that if you create a (regular, local) branch whose name is origin/Bruce —this is a bad idea, but it does happen by accident—you can name origin/Bruce (the branch) differently from origin/Bruce (a remote-tracking branch with the same short name, but a different full name). 此名称具有“全名”变体,因此,如果您创建一个名称为origin/Bruce的(常规,本地)分支(这是个坏主意,但确实会偶然发生),则可以命名origin / Bruce(分支)与origin / Bruce(具有相同名称但名不同的远程跟踪分支 )不同。 The full name is what you get if you write refs/heads/ in front, ie, refs/heads/master or, in the case of the poorly named Bruce branch, refs/heads/origin/Bruce . 全名就是您在前面写refs/heads/得到的名称,即refs/heads/master或者在名称不佳的Bruce分支中是refs/heads/origin/Bruce

Git tends to strip off the refs/heads/ part when showing your branch names, since it is normally not needed. 在显示分支名称时,Git倾向于剥离refs/heads/部分,因为通常不需要它。 (It may matter with unfortunately-named branches like origin/Bruce , but Git still strips off the prefix.) (这对不幸命名的分支(例如origin/Bruce可能很重要,但是Git仍然会删除前缀。)

Definition: remote-tracking branch 定义:远程跟踪分支

A remote-tracking branch is just a name whose full-name form starts with refs/remotes/ and then includes the name of a remote. 远程跟踪分支只是一个名称,其全名形式以refs/remotes/开头,然后包括远程名称。 Hence refs/remotes/origin/master is a remote-tracking branch for branch master as found on remote origin , while refs/remotes/origin/Bruce is a remote-tracking branch for branch Bruce . 因此refs/remotes/origin/master为分支的远程跟踪分支master远程如发现origin ,而refs/remotes/origin/Bruce为分支的远程跟踪分支Bruce Git tends to strip off the refs/remotes/ when showing you such names. 当向您显示这样的名称时,Git倾向于剥离refs/remotes/

Definition: hash (SHA-1) and object (commit, tree, blob, annotated-tag) 定义:哈希(SHA-1)和对象(commit,tree,blob,带注释的标签)

A Git hash is one of those big ugly SHA-1 strings like 3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243 . Git 哈希是那些大型丑陋的SHA-1字符串之一,例如3ad15fd5e17bbb73fb1161ff4e9c3ed254d5b243 These are the true names of the entities that git keeps internally. 这些是git内部保留的实体的真实名称。 Every commit has a unique hash; 每个提交都有唯一的哈希值; in fact, every unique object has a unique hash. 实际上,每个唯一对象都有一个唯一的哈希。

An object , in Git, is an internal storage item whose name is one of Git's hashes. Git中的对象是内部存储项目,其名称是Git的哈希值之一。 There are four kinds of objects; 有四种对象; the two most interesting ones here are "commits" (which store commits) and "blobs" (which store file content). 这里最有趣的两个是“ commits”(存储提交)和“ blobs”(存储文件内容)。 The other two are "tree" objects (which produce name to ID mappings, so that git can tell that, eg, a blob named 9a31f... should be made accessible under the name myfile.txt , for instance) and annotated-tag objects (which store annotated tags, which normally point to commits). 另外两个是“树”对象(产生名称到ID映射的对象,以便git可以告诉,例如,应使名为9a31f...的blob在名称myfile.txt下可访问)和带注释的标签对象(存储带注释的标签,通常指向提交)。

Definition: reference 定义:参考

A reference , in Git, is just the general, full-name form used for branches and tags, and several other things as well. 在Git中, 引用只是用于分支和标签以及其他一些东西的通用全名形式。 What we care about here is mostly just branches. 我们在这里关心的主要只是分支。 You can usually use short names—the exceptions occur when using plumbing commands like git update-ref , or when you have unfortunate names like the two origin/Bruce s—and Git will figure out the full name for you. 通常,您可以使用短名称(使用git update-ref等管道命令或使用两个origin/Bruce不幸的名称时会发生例外),Git会为您找出全名。

Definition: refspec 定义:refspec

A Git refspec is just a pair of references separated by a colon : , with an optional leading plus sign. Git refspec只是一对引用,中间用冒号:分隔,并带有可选的前导加号。 The reference on the left is the src (source) reference, and the one on the right is the dst (destination). 左边的参考是src (源)参考,右边的是dst (目标)。 In some, but not all, cases, you can omit either src or : dst . 在某些(但不是全部)情况下,可以省略src: dst When leaving out the : dst part, if there is no leading + , it looks just like a reference name, and you just have to know that it is actually a refspec instead. 省略: dst部分时,如果没有前导+ ,则它看起来就像是引用名称,您只需要知道它实际上是refspec即可。

Definition: ancestor 定义:祖先

One commit is an ancestor of another if, by following all the various parent ID pointers, we can get from the second commit back to the first commit. 如果通过遵循所有各种父ID指针,我们可以从第二个提交回到第一个提交,那么一个提交就是另一个提交的祖先 For Git's purposes, a commit is also an ancestor of itself. 出于Git的目的,提交也是其自身的祖先。 Hence a commit is its own parent and its own child, while its parent is only its parent. 因此,提交是其自身的父级其子级,而其父级只是其父级。 A grandparent is an ancestor; 祖父母是祖先;祖父母是祖先。 a child or grandchild is not. 子孙不是。

1> I think if you change your branch and then do a commit, it will be pushed to that particular branch itself: 1>我认为,如果您更改分支然后执行提交,它将被推送到该特定分支本身:

git checkout A git checkout A
git commit -m "message" git commit -m“消息”
git push origin A:refs/for/A git push origin A:参考/用于/ A

2> if you have already done the commit, you can revert it first, then change the branch followed by a commit and push: 2>如果您已经完成提交,则可以先将其还原,然后再更改分支,然后再提交并推送:

git reset --soft HEAD~ git reset --soft HEAD〜
git checkout A git checkout A
git commit -m "message" git commit -m“消息”
git push origin A:refs/for/A git push origin A:参考/用于/ A

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 通过特定的提交隔离git从一个分支到另一个分支的更改 - Isolate git changes from one branch to another by specific commit git 将更改从一个提交应用到另一个分支 - git apply changes from one commit onto another branch 如何在Visual Studio中打开另一个GIT分支而无需提交对当前的更改? - How can one in Visual Studio open another GIT-branch without need to commit changes to the current one? git如何仅将一次提交中的新更改合并到另一个分支中? - git how to merge just the new changes in a single commit into another branch? Git:如何像将其与另一个分支合并一样提交更改? - Git: how to commit changes as if it were a merge with another branch? git:远程HEAD模棱两可-如何将更改推送到克隆分支之外的另一个分支? - git : remote HEAD is ambiguous - how to push changes to another branch than the cloned one? git-将暂存的更改提交到另一个分支并合并 - git - Commit staged changes to another branch and merge 如何从一个远程分支拉到另一个分支? - How to pull from one remote branch to another branch in git? 如何从一个git分支更改到另一个分支 - How to take changes from one git branch to another branch 如何通过git将更改从一个分支拉到另一个分支 - How to pull changes from one branch to another branch through git
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM