[英]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
避免将实际配置放入存储库中。
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
以查看它是否是分支或标记,并扩展dst
以refs/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甚至不必费心提供这些。
A remote is simply a name, like origin
or upstream
, under which you store various entries in your local repository's configuration. 远程只是一个名称,例如
origin
或upstream
,您可以在其下将各种条目存储在本地存储库的配置中。 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
。
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
. 我在这里使用的含义是可移动指针的含义,更具体地说是分支名称 ,例如
master
或develop
。 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仍然会删除前缀。)
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/
。
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
下可访问)和带注释的标签对象(存储带注释的标签,通常指向提交)。
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会为您找出全名。
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即可。
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/Agit 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 Agit checkout A
git commit -m "message"git commit -m“消息”
git push origin A:refs/for/Agit push origin A:参考/用于/ A
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.