Common branch operations of Git

Posted by TecBrat on Sat, 20 Jun 2020 05:20:03 +0200

catalog

Branch Management

The branch is the parallel universe in science fiction movies. When you are trying to learn Git in front of the computer, you are trying to learn SVN in another parallel universe.

If the two parallel universes don't interfere with each other, it doesn't affect you. However, at some point in time, the two parallel universes merge, and as a result, you learn both Git and SVN!

What is the use of branching in practice? Suppose you are going to develop a new function, but it will take two weeks to complete. In the first week, you wrote 50% of the code. If you submit it immediately, because the code has not been written, the incomplete code base will cause others to be unable to work. If you wait for the code to be written and submitted again, there is a huge risk of losing your daily progress.

Now that you have branches, don't be afraid. You have created a branch of your own, which can't be seen by others, and you continue to work normally on the original branch. When you work on your own branch, you can submit it if you want to. After the development, you can merge it into the original branch at one time, which is safe and does not affect others' work.

Other version control systems, such as SVN, have branch management, but after using them, you will find that these version control systems are slower than snails in creating and switching branches, which is intolerable. As a result, the branch function has become a device that everyone does not use.

But Git's branches are different. Whether you create, switch or delete branches, Git can be completed in one second! Whether your version library is one file or 10000 files.

1. Create and merge branches

In version fallback, you already know that Git strings them into a timeline, which is a branch, every time it submits. Up to now, there is only one timeline. In Git, this branch is called the master branch. Strictly speaking, the HEAD does not point to submission, but to the master, which points to submission. Therefore, the HEAD points to the current branch.

At the beginning, the master branch is a line. Git uses master to point to the latest commit, and HEAD to point to master to determine the current branch and the commit point of the current branch:

Each time you commit, the master branch moves forward a step, so as you commit, the line of the master branch becomes longer and longer.

When we create a new branch, such as dev, Git creates a new pointer called dev, points to the same commit of the master, and then points the HEAD to dev, which means that the current branch is on dev:

You see, Git is quick to create a branch, because except for adding a dev pointer, changing the direction of the HEAD, there is no change in the file of the workspace!

However, from now on, the modification and submission of the workspace is aimed at the dev branch. For example, after a new submission, the dev pointer moves forward a step, while the master pointer does not change:

If our work on dev is finished, we can merge dev into master. How does Git merge? The simplest way is to directly point the master to the current commit of dev to complete the merge:

So Git merges branches quickly! Just change the pointer and the content of the workspace will not change!

After merging the branches, you can even delete the dev branch. To delete the dev branch is to delete the dev pointer. After deleting, we have a master branch:

It's amazing. Can you see that some submissions are done through branches?

Let's start the actual battle.

First, we create the dev branch, and then switch to the dev branch:

$ git checkout -b dev
Switched to a new branch 'dev'

The git checkout command plus the - b parameter indicates creation and switching, which is equivalent to the following two commands:

$ git branch dev
$ git checkout dev
Switched to branch 'dev'

Then, use the git branch command to view the current branch:

$ git branch
* dev
  master

The git branch command lists all branches, and the current branch is preceded by a * sign.

Then, we can commit normally on the dev branch, for example readme.txt Make a change and add a line:

Creating a new branch is quick.

Then submit:

$ git add readme.txt 
$ git commit -m "branch test"
[dev b17d20e] branch test
 1 file changed, 1 insertion(+)

Now that the dev branch is finished, we can switch back to the master branch:

$ git checkout master
Switched to branch 'master'

After switching back to the master branch, view another readme.txt File, the content just added is missing! Because the commit is on the dev branch, and the commit point of the master branch does not change at this moment:

Now, let's combine the work results of the dev branch to the master branch:

$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

The git merge command is used to merge the specified branch to the current branch. View after merging readme.txt As you can see, this is exactly the same as the latest commit of the dev branch.

$ cat read.TXT
 Seize the day, never lose the time!
Git is a version control system.
Nothing is difficult if you put your heart into it
 This is MXS
zx
xjg
 June 17, 2020

Notice the fast forward information above. Git tells us that the merge is "fast forward mode", that is, direct the master to the current commit of dev, so the merge speed is very fast.

Of course, not every merger can be fast forward. We will talk about other ways of merger later.

After the merge, you can safely delete the dev branch:

$ git branch -d dev
Deleted branch dev (was b17d20e).

After deleting, view the branch, and only the master branch is left:

$ git branch
* master

Because creating, merging and deleting branches are very fast, Git encourages you to use branches to complete a task, and then delete branches after merging. This is the same as working directly on the master branch, but the process is safer.

1.2switch

We notice that git checkout < Branch > is used to switch branches, while git checkout -- < File > is the undo modification mentioned above. The same command has two functions, which is a bit confusing.

In fact, it's more scientific to switch branches. Therefore, the latest version of Git provides a new git switch command to switch branches:

To create and switch to a new dev branch, you can use:

$ git switch -c dev

Switch directly to the existing master branch. You can use:

$ git switch master

Using the new git switch command is easier to understand than git checkout.

2. Conflict resolution

Nine times out of ten, the merger of branches is not always smooth.

Prepare a new feature1 branch and continue our new branch development:

$ git switch -c feature1
Switched to a new branch 'feature1'

modify readme.txt The last line should read:

Creating a new branch is quick AND simple.

Commit on the feature1 branch:

$ git add readme.txt

$ git commit -m "AND simple"
[feature1 14096d0] AND simple
 1 file changed, 1 insertion(+), 1 deletion(-)

Switch to the master branch:

$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Git will also automatically prompt us that the current master branch is 1 commit ahead of the remote master branch.

On the master branch readme.txt The last line of the file should read:

Creating a new branch is quick & simple.

Submitted by:

$ git add readme.txt 
$ git commit -m "& simple"
[master 5dc6824] & simple
 1 file changed, 1 insertion(+), 1 deletion(-)

Now, both the master branch and the feature1 branch have their own new submissions, which are as follows:

In this case, Git cannot perform "quick merge" and can only try to merge their own changes. However, there may be conflicts in this merge. Let's try:

$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

As expected, there is a conflict! Git told us, readme.txt There is a conflict in the file. You must resolve the conflict manually before submitting it. git status can also tell us about the conflicting files:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

	both modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

We can see it directly readme.txt Content of:

Seize the day, never lose the time!
Git is a version control system.
Nothing is difficult if you put your heart into it
 This is MXS
zx
xjg
<<<<<<< HEAD
000
=======
June 17, 2020
0000
>>>>>>> featurel

Git marks the contents of different branches with < < < < ========================, > > > > > and > > we modify them as follows and save them:

June 17, 2020

Resubmission:

$ git add readme.txt 
$ git commit -m "conflict fixed"
[master cf810e4] conflict fixed

Now, the master and feature1 branches become the following:

With git log with parameters, you can also see the merging of branches:

$ git log --graph --pretty=oneline --abbrev-commit
*   99fa0a0 (HEAD -> master) ccc
|\
| * 5174635 (featurel) 8zx
* | aa309c8 fff
|/
* 06e428d (origin/master) remove love.TXT
* 284196c git track
* e0b6320 understand how stage works
* a2092c9 3
* 747bea0 2
* e5b5712 1
* 021749f mxs
* 34ec753 zx
* af87

Finally, delete the feature1 branch:

$ git branch -d feature1
Deleted branch feature1 (was 14096d0).

3. Branch management strategy

Generally, when merging branches, Git will use Fast forward mode if possible, but in this mode, after deleting branches, it will lose branch information.

If you force the Fast forward mode to be disabled, Git will generate a new commit at the time of merge, so that you can see the branch information from the branch history.

Now let's fight -- git merge in no FF mode:

First, still create and switch the dev branch:

$ git switch -c dev
Switched to a new branch 'dev'

modify readme.txt File and submit a new commit:

$ git add readme.txt 
$ git commit -m "add merge"
[dev f52c633] add merge
 1 file changed, 1 insertion(+)

Now, let's switch back to master:

$ git switch master
Switched to branch 'master'

To merge dev branches, note the -- no FF parameter, which means Fast forward is disabled:

$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

Because a new commit is to be created in this merge, add the - m parameter and write in the commit description.

After merging, let's use git log to see the branch history:

$ git log --graph --pretty=oneline --abbrev-commit
*   e1e9c68 (HEAD -> master) merge with no-ff
|\  
| * f52c633 (dev) add merge
|/  
*   cf810e4 conflict fixed
...

As you can see, without Fast forward mode, after merging, it looks like this:

4. Branch strategy

In the actual development, we should carry out branch management according to several basic principles:

First of all, the master branch should be very stable, that is to say, it is only used to release new versions and cannot work on them normally;

Where do you work? The work is all on the dev branch, that is to say, the dev branch is unstable. At some time, for example, when version 1.0 is released, you can merge the dev branch into the master and release version 1.0 in the master branch;

You and your friends are all working on the dev branch. Everyone has his own branch. You can merge with the dev branch from time to time.

So the branch of teamwork looks like this:

5.Bug branch

In software development, bugs are just as common. Bugs need to be fixed. In Git, because branches are so powerful, every bug can be fixed by a new temporary branch. After the repair, merge the branches, and then delete the temporary branches.

When you receive a task to fix a bug named 101, it is natural that you want to create a branch issue-101 to fix it, but, etc., the work currently in progress on dev has not been committed:

$ git status
On branch dev
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   hello.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   readme.txt

It's not that you don't want to submit, but that the work is only half done and can't be submitted. It's expected to take another day to complete. However, the bug must be fixed within two hours. What should I do?

Fortunately, Git also provides a stash function, which can "store" the current work site and continue to work after the site is restored in the future:

$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge

Now, viewing the workspace with git status is clean (unless there are files that are not managed by Git), so you can safely create branches to fix bug s.

First, determine which branch you want to fix the bug on. Assuming that you need to fix it on the master branch, create a temporary branch from the master:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git checkout -b issue-101
Switched to a new branch 'issue-101'

Now to fix the bug, you need to change "000." to "00", and then submit:

$ git add readme.txt 
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

After the repair, switch to the master branch, complete the merge, and finally delete the issue-101 branch:

$ git switch master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
  (use "git push" to publish your local commits)

$ git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Great, the original two-hour bug fix only took five minutes! Now, it's time to get back to the dev branch!

$ git switch dev
Switched to branch 'dev'

$ git status
On branch dev
nothing to commit, working tree clean

The working area is clean. Where was the working site just stored? Use the git stash list command to see:

$ git stash list
stash@{0}: WIP on dev: f52c633 add merge

The work site is still there. Git has saved the stash content somewhere, but it needs to be restored. There are two ways:

First, use git stash apply to restore, but after the restore, the stash content will not be deleted. You need to use git stash drop to delete;

Another way is to use git stash pop to restore the stash content at the same time

$ git stash pop
On branch dev
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   hello.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   readme.txt

Dropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a)

Then check the git stash list, and you will not see any stash content:

$ git stash list

You can stash multiple times. When restoring, first use git stash list to view, and then restore the specified stash with the command:

$ git stash apply stash@{0}

After fixing the bug on the master branch, let's think about that the dev branch was separated from the master branch in the early stage, so this bug actually exists on the current dev branch.

How to fix the same bug on the dev branch? Repeat the operation once. Can't you just submit it?

Is there a simpler way to do it?

yes!

For the same bug, to fix it on dev, we only need to "copy" the changes made by 4c805e2 fix bug 101 to dev branch. Note: we just want to copy the changes made by 4c805e2 fix bug 101, not merge the whole master branch.

For ease of operation, Git provides a cherry pick command, which allows us to copy a specific commit to the current branch:

$ git branch
* dev
  master
$ git cherry-pick 4c805e2
[master 1d4b803] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

Git automatically commits the dev branch once. Note that the commit of this commit is 1d4b803, which is different from 4c805e2 of the master, because the two commit changes are the same, but they are actually two different commit. With git cherry pick, we don't need to manually repeat the bug fixing process on the dev branch.

Some smart children's shoes will think, since you can "replay" this repair process on the dev branch after you fix the bug on the master branch, can you fix the bug directly on the dev branch and then "replay" it on the master branch? Sure, but you still need the git stash command to save the scene to switch from the dev branch to the master branch.

6.Feature branch

In software development, there are always endless new functions to be added.

When adding a new feature, you certainly don't want to confuse the main branch because of some experimental code. Therefore, it is better to create a new feature branch and develop it on it. After completion, merge and finally delete the feature branch.

Now, you finally have a new mission: to develop a new feature called Vulcan, which is planned for the next generation of starships.

So we are ready to develop:

$ git switch -c feature-vulcan
Switched to a new branch 'feature-vulcan'

After 5 minutes, the development is completed:

$ git add vulcan.py

$ git status
On branch feature-vulcan
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   vulcan.py

$ git commit -m "add feature vulcan"
[feature-vulcan 287773e] add feature vulcan
 1 file changed, 2 insertions(+)
 create mode 100644 vulcan.py

Switch back to dev and prepare to merge:

$ git switch dev

If all goes well, the feature branch and the bug branch are similar, merge and delete.

But!

At this time, received orders from the superior, due to insufficient funds, the new function must be cancelled!

In spite of this, the branch containing confidential information must be destroyed in place:

$ git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-vulcan'.

Destroy failed. Git friendly reminder, feature Vulcan branch has not been merged, if deleted, the modification will be lost, if you want to forcibly delete, you need to use the uppercase - D parameter..

Now let's force the deletion:

$ git branch -D feature-vulcan
Deleted branch feature-vulcan (was 287773e).

Finally deleted!

7. Multi person cooperation

When you clone from a remote warehouse, Git automatically corresponds the local master branch to the remote master branch, and the default name of the remote warehouse is origin.

To view information about a remote library, use git remote:

$ git remote
origin

Or, use git remote -v to display more detailed information:

$ git remote -v
origin  https://github.com/ITmxs/mygit.git (fetch)
origin  https://github.com/ITmxs/mygit.git (push)

The address of the origin that can be crawled and pushed is shown above. If you do not have push permission, you will not see the address of the push.

8. Push branch

Push branch is to push all local commits on the branch to the remote library. When pushing, specify the local branch, so Git will push the branch to the corresponding remote branch of the remote library:

$ git push origin master

If you want to push other branches, such as dev, change to:

$ git push origin dev

However, it is not necessary to push local branches to remote locations. So, which branches need to be pushed and which ones do not?

  • The master branch is the main branch, so it should be synchronized with the remote at all times;
  • dev branch is a development branch, all members of the team need to work on it, so it also needs to be synchronized with remote;
  • The bug branch is only used to fix bugs locally, so there's no need to push it to remote, unless the boss wants to see how many bugs you fix every week;
  • Whether feature branches are pushed remotely depends on whether you work with your small partners to develop them.

In short, in Git, branches can hide and play in the local area. Whether to push or not depends on your mood!

9. Grab branch

When multiple people work together, they push their own changes to the master and dev branches.

Now, to simulate a small partner, you can clone it on another computer (note to add SSH Key to GitHub) or another directory of the same computer:

$ git clone git@github.com:michaelliao/learngit.git
Cloning into 'learngit'...
remote: Counting objects: 40, done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 40 (delta 14), reused 40 (delta 14), pack-reused 0
Receiving objects: 100% (40/40), done.
Resolving deltas: 100% (14/14), done.

When your small partner is from the remote library clone, by default, your small partner can only see the local master branch. If you don't believe it, you can use the git branch command:

$ git branch
* master

Now, to develop on the dev branch, your little partner must create the dev branch of the remote origin to the local, so he uses this command to create the local dev branch:

$ git checkout -b dev origin/dev

Now, he can continue to modify on dev, and then, from time to time, push the dev branch to the remote:

$ git add env.txt

$ git commit -m "add env"
[dev 7a5e5dd] add env
 1 file changed, 1 insertion(+)
 create mode 100644 env.txt

$ git push origin dev
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 308 bytes | 308.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
   f52c633..7a5e5dd  dev -> dev

Your little partner has pushed his submission to the origin/dev branch, and it happens that you have modified the same file and tried to push:

$ cat env.txt
env

$ git add env.txt

$ git commit -m "add new env"
[dev 7bd91f1] add new env
 1 file changed, 1 insertion(+)
 create mode 100644 env.txt

$ git push origin dev
To github.com:michaelliao/learngit.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Push failed because there is a conflict between the latest submission of your little partner and the submission you are trying to push. The solution is simple. Git has already prompted us to use git pull to catch the latest Submission from origin/dev, then merge locally to resolve the conflict, and then push:

$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

    git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=origin/<branch> dev

git pull also failed because the link between the local dev branch and the remote origin/dev branch is not specified. According to the prompt, set the link between Dev and origin/dev:

$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

Then pull:

$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt
Automatic merge failed; fix conflicts and then commit the result.

This time git pull succeeds, but there are conflicts in the merge, which need to be solved manually. The solution and the Conflict resolution It's exactly the same. After settlement, submit and push

$ git commit -m "fix env conflict"
[dev 57c53ab] fix env conflict

$ git push origin dev
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
   7a5e5dd..57c53ab  dev -> dev

Therefore, the working mode of multi person cooperation is usually as follows:

  1. First, you can try to push your own changes with git push origin < branch name >;
  2. If the push fails, because the remote branch is newer than your local one, you need to use git pull to try to merge;
  3. If there is a conflict in the merge, resolve the conflict and submit locally;
  4. After there is no conflict or the conflict is resolved, you can use git push origin < branch name > to push successfully!

If git pull prompts no tracking information, it means that the link relationship between the local branch and the remote branch has not been created. Use the command git branch -- set upstream to < branch name > origin / < branch name >.

This is the working mode of multi person cooperation. Once familiar with it, it is very simple.

10.Rebase

As we saw in the previous section, when multiple people work together on the same branch, it's easy to have conflicts. Even if there is no conflict, the post push children's shoes have to be pull ed first, merged locally, and then pushed successfully.

After each merge and push, the branch becomes as follows:

$ git log --graph --pretty=oneline --abbrev-commit
* d1be385 (HEAD -> master, origin/master) init hello
*   e5e69f1 Merge branch 'dev'
|\  
| *   57c53ab (origin/dev, dev) fix env conflict
| |\  
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
| |/  
* |   12a631b merged bug fix 101
|\ \  
| * | 4c805e2 fix bug 101
|/ /  
* |   e1e9c68 merge with no-ff
|\ \  
| |/  
| * f52c633 add merge
|/  
*   cf810e4 conflict fixed

All in all, it looks messy. Children's shoes with obsessive-compulsive disorder ask: why can't Git's submission history be a clean line?

In fact, it can be done!

Git has an operation called rebase, which someone translates as "rebase.".

Don't start imagining. Let's start from the practical problem and see how to turn the forked submission into a straight line.

After synchronizing with the remote branch, we hello.py This document has been submitted twice. Use the git log command to see:

$ git log --graph --pretty=oneline --abbrev-commit
* 582d922 (HEAD -> master) add author
* 8875536 add comment
* d1be385 (origin/master) init hello
*   e5e69f1 Merge branch 'dev'
|\  
| *   57c53ab (origin/dev, dev) fix env conflict
| |\  
| | * 7a5e5dd add env
| * | 7bd91f1 add new env
...

Note that Git uses (HEAD - > Master) and (origin/master) to identify the location of the HEAD and remote origin of the current branch, which are 582d922 add author and d1be385 init hello, respectively. The local branch commits two times faster than the remote branch.

Now let's try to push the local branch:

$ git push origin master
To github.com:michaelliao/learngit.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Unfortunately, it failed, which means someone pushed the remote branch ahead of us. According to experience, first pull:

$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:michaelliao/learngit
   d1be385..f005ed4  master     -> origin/master
 * [new tag]         v1.0       -> v1.0
Auto-merging hello.py
Merge made by the 'recursive' strategy.
 hello.py | 1 +
 1 file changed, 1 insertion(+)

Use git status to see the status:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

With the commit we just merged, now our local branch is 3 commits ahead of the remote branch.

Use git log to see:

$ git log --graph --pretty=oneline --abbrev-commit
*   e0ea545 (HEAD -> master) Merge branch 'master' of github.com:michaelliao/learngit
|\  
| * f005ed4 (origin/master) set exit=1
* | 582d922 add author
* | 8875536 add comment
|/  
* d1be385 init hello
...

For the children's shoes with obsessive-compulsive disorder, things are a little bit wrong now. The history of submission is split. If you push the local branch to the remote now, is there any problem?

yes!

What's the problem?

disgraceful!

Is there a solution?

yes!

At this point, rebase comes in handy. Let's try the command git rebase:

$ git rebase
First, rewinding head to replay your work on top of it...
Applying: add comment
Using index info to reconstruct a base tree...
M	hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py
Applying: add author
Using index info to reconstruct a base tree...
M	hello.py
Falling back to patching base and 3-way merge...
Auto-merging hello.py

Output a lot of operations, what is the effect? Then use git log to see:

$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master) add author
* 3611cfe add comment
* f005ed4 (origin/master) set exit=1
* d1be385 init hello
...

The original split submission now becomes a straight line! How does this magic work? In fact, the principle is very simple. We noticed that Git moved our local submission to f005ed4 (origin/master) set exit=1, so that the whole submission history became a straight line. Before and after the rebase operation, the final commit content is consistent. However, our local commit modifications have changed. Their modifications are no longer based on the d1be385 init hello, but on the f005ed4 (origin/master) set exit=1, but the final commit content of 7e61ed4 is consistent.

This is the characteristic of rebase operation: it is more intuitive to "organize" the forked submission history into a straight line. The disadvantage is that the local fork commit has been modified.

Finally, push the local branch to the remote:

$ git push origin master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 576 bytes | 576.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To github.com:michaelliao/learngit.git
   f005ed4..7e61ed4  master -> master

Use git log to see the effect:

$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master, origin/master) add author
* 3611cfe add comment
* f005ed4 set exit=1
* d1be385 init hello
...

How does this magic work? In fact, the principle is very simple. We noticed that Git moved our local submission to f005ed4 (origin/master) set exit=1, so that the whole submission history became a straight line. Before and after the rebase operation, the final commit content is consistent. However, our local commit modifications have changed. Their modifications are no longer based on the d1be385 init hello, but on the f005ed4 (origin/master) set exit=1, but the final commit content of 7e61ed4 is consistent.

This is the characteristic of rebase operation: it is more intuitive to "organize" the forked submission history into a straight line. The disadvantage is that the local fork commit has been modified.

Finally, push the local branch to the remote:

$ git push origin master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 576 bytes | 576.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To github.com:michaelliao/learngit.git
   f005ed4..7e61ed4  master -> master

Use git log to see the effect:

$ git log --graph --pretty=oneline --abbrev-commit
* 7e61ed4 (HEAD -> master, origin/master) add author
* 3611cfe add comment
* f005ed4 set exit=1
* d1be385 init hello
...

The commit history of the remote branch is also a straight line.

Topics: git github svn ssh