简体   繁体   English

如何重置master并将分支保留在git中?

[英]How do I reset master and keep my branch in git?

Suppose I do 假如我做

$ git checkout master
$ touch foo.py
$ git commit -m "oops" foo.py
$ git checkout -b new_branch
$ touch bar.py
$ git commit -m "changes" bar.py

Now when I try to push back changes on new_branch, I get 现在,当我尝试推迟对new_branch的更改时,我得到

Local branch 'master' is ahead of remote branch 'origin/master'

How do I reset master while not losing my changes (foo.py, bar.py) on new_branch? 如何在不丢失new_branch上的更改(foo.py,bar.py)的同时重置master?

I read the git reset page , and it looked like it might involve --keep, but I couldn't tell. 我阅读了git reset页面 ,看起来可能涉及--keep,但我不知道。

This can be very confusing initially, and what you needed was a proper introduction to how Git implements branches; 最初这可能会非常混乱,您需要的是对Git如何实现分支的正确介绍。 but at this point we'll use the retrofit method. 但目前我们将使用改造方法。 :-) The trick to understanding all this is that Git's commits are permanent and unchanging, but its branches —or more precisely, branch names —are temporary, and in fact mostly irrelevant. :-)理解所有这些技巧的窍门是Git的提交是永久的且不变的,但是它的分支(或更确切地说是分支名称 )是临时的,实际上实际上是无关紧要的。

There are three things that matter when you are building a new commit (they are HEAD , the index , and the work-tree ), but once you have the commit built and committed, it's quite permanent and it is very hard to get Git to lose it entirely. 在构建新的提交时,有三件事很重要(它们是HEADindexwork-tree ),但是一旦构建并提交了提交,它便是永久性的,很难将Git提交给它。完全失去它。 It's easy enough to accidentally misplace it, though, so let's try to avoid that. 不过,很容易将其意外放错位置,因此,我们尝试避免这种情况。 :-) :-)

If we ignore the branch names entirely, we can draw a graph of the commits that exist in your repository. 如果我们完全忽略分支名称,则可以绘制存储库中存在的提交的图形 Given what you have done—making two new commits—let's draw them like this, where round o s represent commits, and A and B are your two new commits: 鉴于你做的决策是什么了两个新的提交,让我们吸取他们这个样子,哪里轮o s为承诺,以及AB是两个的提交:

...--o--o--o
            \
             A
              \
               B

We could draw them all on one line, but I want to leave room to write labels in on the right. 我们可以将它们全部画在一条线上,但是我想留出空间在右侧写标签。 Commit A is your "oops", and B is your "changes". 提交A是您的“哎呀”,而B是您的“变更”。

The main noteworthy thing about this graph drawing is that each commit points to (stores the hash ID of) its predecessor commit. 有关该图形绘制的主要注意事项是,每个提交都指向其先前的提交(存储其哈希ID)。 This means that commit B points back to commit A . 这意味着提交B指向提交A Commit A points back to the next-most-recent commit, which points back still further, and so on. 提交A指向最近的下一个提交,指向更远的位置,依此类推。

Now we add the labels—the branch names . 现在,我们添加标签- 分支名称 The last boring commit o still probably has a label origin/master . 最后一次无聊的提交o仍可能带有标签origin/master Commit A has the label master , and commit B has the label new_branch , so let's draw these in: 提交A的标签为master ,提交B的标签为new_branch ,因此我们将其绘制为:

...--o--o--o   <-- origin/master
            \
             A   <-- master
              \
               B   <-- new_branch (HEAD)

This is what branch names are and do for you: they are pointers to commits; 这就是分支名称为您服务的名称:它们是提交的指针; they remember each commit's big ugly hash ID for you. 他们会记住每个提交的丑陋哈希ID。

When you are on some branch and make a new commit, the branch name comes along for the ride. 当您在某个分支上并进行提交时,分支名称随之出现。 The special name HEAD remembers which branch you're on, so that Git knows which name to move to the new commit. 特殊名称HEAD记住您所在的分支,以便Git知道要移至新提交的名称。 (We needed this temporarily at least, when master and new_branch briefly both pointed to commit A .) (至少在masternew_branch短暂地指向commit A时,我们暂时需new_branch )。

What you want to do now is move master back to point to the last of the boring o commits. 你想现在做的是移动master 点到最后的枯燥o提交。 To do so, you can use git reset , which lets you move a name in an arbitrary way: 为此,您可以使用git reset ,它允许您以任意方式移动名称:

git checkout master
git reset --hard origin/master

This assumes (see final section below) that origin/master really does point to the last of the boring o commits. 假设 (请参阅下面的最后部分) origin/master节点确实指向了无聊的o提交的最后一个。 The git reset --hard says: Wipe out my current index and work-tree, and move my current branch—according to HEAD —so that it points to the commit I name here. git reset --hard表示: 清除当前索引和工作树,并根据HEAD移动当前分支,使其指向我在此处命名的提交。 We must git checkout master first , so that HEAD names master . 我们必须git checkout master 至上 ,让HEADmaster Then the git reset does this: 然后git reset这样做:

...--o--o--o   <-- master (HEAD), origin/master
            \
             A
              \
               B   <-- new_branch

So now your two new commits A and B are found only via new_branch and not via master . 因此,现在仅通过new_branch而不是master可以找到您的两个新提交AB

(There are several more things that branch names do for you. In particular, they protect commits from being removed by Git's "garbage collector". If a commit has any name by which we can find it, it's protected. If it has no name, it's no longer protected. There are some semi-hidden names that protect everything for a while—at least 30 days by default—to make sure commits don't get trashed accidentally, but finding them through these reflog names is annoying, so we try not to rely on it so much. (分支名称还可以为您做几件事情。特别是,它们可以防止提交被Git的“垃圾收集器”删除。如果某个提交具有可以找到它的名称,那么它将受到保护。如果它没有名称, ,它不再受保护。有些半隐藏的名称可以保护所有时间(默认情况下至少为30天),以确保不会意外删除提交,但是通过这些reflog名称查找它们很烦人,因此我们尽量不要过分依赖它。

The branch names are also used for git push , so there they matter a fair bit.) 分支名称也用于git push ,因此它们很重要。)

(I mention "trashing" the index and work-tree above, which isn't completely true. The git reset command, used the way we're using it here, has three jobs it can do: (我提到“破坏”了上面的索引和工作树,这并不完全正确git reset命令使用了我们在此处使用的方式,可以完成三个工作:

  • move the current branch 移动当前分支
  • reset the index 重置索引
  • reset the work-tree 重置工作树

It always does the first, optionally adds the second job, and then optionally adds the third. 它始终执行第一项,可选地添加第二项工作,然后可选地添加第三项工作。 The --hard mode does all three, the --mixed mode does the first two, and the --soft mode does only one. --hard模式执行全部三个操作,-- --mixed模式执行前两个操作,而--soft模式仅执行一个操作。 To understand them all properly, we would have to look closely at the definitions of index and work-tree and I'll leave that for other SO questions/answers. 为了正确理解它们,我们必须仔细查看索引工作树的定义,我将其留给其他SO问题/答案。 The key item here, though, is that you don't want to git reset --hard until you have everything saved away in commits.) 不过,这里的关键项是您希望git reset --hard直到您将所有内容都保存在提交中为止。)

What if there's no origin/master (or it points back too far)? 如果没有origin/master (或者指向太远)怎么办?

We assumed, above, that there was a handy label—a so-called remote-tracking branch —identifying the commit we want to git reset our master to point-to. 上面我们假设有一个方便的标签-所谓的远程跟踪分支 -标识了我们要git reset提交的提交, git reset master git reset为指向。 If we don't have this label, or it points to an even earlier commit, we must locate that commit some other way. 如果没有该标签,或者它指向一个更早的提交,则必须以其他方式定位该提交。

There are a lot of ways to do this. 有很多方法可以做到这一点。 The most straightforward is to go by its hash ID. 最简单的方法是使用其哈希ID。 If you run git log while on some branch, you see each commit that is "on" or "contained in" that branch, normally in Git's usual backwards-going order. 如果在某个分支上运行git log ,则会看到该分支“上”或“包含在”该分支的每个提交,通常按Git通常的向后顺序。 For instance, we might git log and see commit B , with its big ugly hash ID, then commit A with its hash ID, and then that boring commit o with its big ugly hash ID. 例如,我们可能会git log并看到具有大丑陋哈希ID的提交B ,然后具有其哈希ID的提交A ,然后是具有大丑陋的哈希ID的无聊的提交o

We can use the raw hash ID: 我们可以使用原始哈希ID:

git checkout master && git reset --hard <hash-id>

if we don't have the label. 如果我们没有标签。 If we do have the label, we should probably just use it. 如果我们有标签,则可能应该使用它。

You might remember to get help from "A DOG" with git log : 您可能还记得使用git log从“ A DOG”获得帮助:

git log --all --decorate --oneline --graph

The a ll makes Git show all branches and all remote-tracking branches (and everything else: tags, the "stash", notes, etc). A ll使Git显示所有分支和所有远程跟踪分支(以及其他所有内容:标签,“存储”,注释等)。 The d ecorate option attaches the label names to commits. d ecorate选项将标签名称附加到提交。 The o neline option makes the output show just one line for each commit, and the g raph option makes Git try to draw the commit graph. o neline选项使输出每次提交仅显示一行,而g raph选项使Git尝试绘制提交图。

How do I reset master while not losing my changes (foo.py, bar.py) on new_branch? 如何在不丢失new_branch上的更改(foo.py,bar.py)的同时重置master?

Yes, foo.py and bar.py is in your new branch. 是的,foo.py和bar.py在您的新分支中。

  • You have created a branch from the master which is not pushed to the origin. 您已经从母版创建了一个分支,但未推送到原点。 so when you try to push the master you got the error because its ahead of remote. 因此,当您尝试推动主机时,您会收到错误消息,因为它位于远程主机之前。

  • You get that message because you made changes in your local master and you didn't push them to remote. 之所以收到该消息,是因为您在本地主机中进行了更改,而没有将其推送到远程主机。

You have several ways to "solve" it and it normally depends on how your workflow looks like: 您可以通过多种方式“解决”它,通常取决于您的工作流程如何:

  • In a good workflow your remote copy of master should be the good one while your local copy of master is just a copy of the one in remote. 在良好的工作流程中,您的master的远程副本应该是好的副本,而您的master的本地副本只是远程副本的副本。 Using this workflow you'll never get this message again. 使用此工作流程,您将永远不会再收到此消息。

  • If you work in another way and your local changes should be pushed then just git push origin assuming origin is your remote 如果您以其他方式工作并且应该推送本地更改,则只需使用git push origin即可,前提是origin是您的远程主机

How I will be doing it : 我将如何做:

git checkout master
git create -b la_lalalla
touch foo.py bar.py
git add drama.py bar.py
git commit -m "Drama commit"

merge the branch to your master, this depends on you to do merge or do rebase, then, 将分支合并到主节点,这取决于您进行合并或变基,然后,

git push origin master. 

your done with adding two files to origin/master. 您完成了将两个文件添加到原点/原版的操作。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM