Git - squash before reboot

I have two branches: master and test-branch (which forks into master ). My work looked something like this:

  • git checkout master
  • git checkout -b test-branch
  • make a bunch of changes and commit them
  • make more changes and make another commit
  • git checkout master
  • git pull other people made changes to master
  • git checkout test-branch
  • git rebase -i master
  • Change everything except the first pick in the interactive console to s
  • I need to resolve two merge conflicts, one for the first commit and one for the second commit

What I would like to do is squash all the commits on test-branch before rebooting, so I only need to resolve merge conflicts once. Is it possible? If so, how?

+5
source share
2 answers

It is possible, even easy. Git is that there are many different ways to do this.

To literally do what you originally proposed:

... crush all commits on the test branch until reboot

The easiest way is if you do this before running git merge , and on the master branch. (I know that you did not specify git merge as a command, but you ran git merge in step 6:

  1. git pull other people made changes to master

because git pull just git fetch followed by git merge .) But it's still pretty easy to do after; we just need to set up the correct commit code.

Make a drawing of the fixation schedule that you have in step 4:

 ...<- o <- * <-- master, origin/master \ A <- B <-- HEAD=test-branch 

This figure shows that there are two labels 1 indicating commit, marked with * , namely master and origin/master . Commit * points to commit o , which points to even more commits: the history of the master branch.

The label for the created branch (and now included, therefore, part of HEAD= ) indicates commit B Commit B then points to commit A , which points to commit * . What is the history of the test-branch : you created it when in * , then added A , then added B

Here are two ways you can easily squash commits A and B at this point:

  • git rebase -i master

    This gives you an interactive editing session in which you can β€œselect” the first commit and then β€œsquash” the second, and it will collect both commit messages together and allow you to edit the result in the usual way. Then it creates a (one) new message whose tree has the value commit B

  • git reset --soft master; git commit

    This does not open an interactive editing session for redirection: it just keeps the staging-area-and-tree from commit B (which is the --soft part of git reset --soft ), moves the test-branch label back to point to the commit * directly (which git reset part of git reset --soft ) and commits git reset --soft as usual ( git commit ).

    The disadvantage is that you need to create a new commit message, but you can restore the commit message from commit A or B any number of ways. For example, you can use the -c or -c flag for git commit (you will need to identify commit A or B , for example, using @{1} or @{1}^ or @{yesterday} or some other reflog specifier). Or, before you do git reset --soft , you can use git log and save the git log messages (logs) to a file or something else.

    (This second method shines if, instead of two squash commits, you have 42 or so.)

Both of these methods really do the same thing: they add a new commit (let it be called AB ), leaving A and B behind and a gray-out view that I cannot draw correctly:

  AB <-- HEAD=test-branch / ...<- o <- * <-- master, origin/master \ A <- B [ghost version of test-branch] 

The phantom version of your branch is invisible to most ordinary users, and ultimately (after 30 days or so by default) the garbage is removed. (Until then, it is still in your repository and in your journals so you can find the original commits A and B if you need them.)

What if you have already done step 6? In this case, you should still identify commit * . You can do it as jsexpert suggested when I wrote this, or you can find it with git merge-base :

 $ mergebase=$(git merge-base HEAD master) # then pick just ONE of the next two $ git rebase -i $mergebase $ git reset --soft $mergebase; git commit 

Here's how it works. After git checkout master; git fetch; git merge; git checkout test-branch git checkout master; git fetch; git merge; git checkout test-branch git checkout master; git fetch; git merge; git checkout test-branch (steps 5 and 6 more or less) your commit schedule now looks something like this:

 ...<- o <- * <- o <-- master, origin/master \ A <- B <-- HEAD=test-branch 

This new o commits that master and origin/master point to - or it could be a whole chain of commits - "on the way", but the merge base is test-branch (where you are now), and master is commit * : the closest common commit before the two branches diverge.

Then we just aim at rebase or reset --soft at that commit. When we finish and have one new AB commit, it looks like this:

  AB <-- HEAD=test-branch / ...<- o <- * <- o <-- master, origin/master \ A <- B [ghost version of test-branch] 

Once you have a compressed AB commit, you can git rebase to master usual way.

Note that another answer does the same thing, it simply identifies commit * by counting commits. If you know that there are two commits between the end of the test-branch , and the "interesting" commit * , HEAD~2 identifies the same commit as $(git merge-base HEAD master) . Using git merge-base allows you to avoid counting.


1 "Links" is a generic term for Git. In this case, they are the names of the branches, and I use the word "label" to distinguish them from the history of the branches formed by this commit graph. The word "branch" in Git is used to mean at least these two different things, and this is confusing.

+3
source

Of course, maybe yoiu just needs to be entered: git rebase -i HEAD~<# of commits to squash>

-i - for interactive reboot. as soon as you do this, you will see vi with instructions on what to do next to commit.

Very detailed messages about this can be found here. Rewrite history :

+1
source

Source: https://habr.com/ru/post/1212742/


All Articles