Thursday, November 19, 2020

git rebase


We should discuss the difference between fast-forward and the 3-way merge before introducing the 'git rebase'


*Fast-forward

When the merging branch (bugFix)  has all commits from the current branch (master), Git will not create a new merging commit. Instead, it will move the 'master' branch pointer to the latest commit of the 'bugfix' branch. It is a line (linear path) relationship.


*3 way merge

When the current branch (master) contains commits that the merging branch (bugfix) doesn't have, Git will create a new merging commit that merges two branches without modifying the existing branches. It is a tree (with a cycle) relationship.


Therefore, if we don't want to make it to create a new commit that is used for merging two branches, then we need to try to make the git structure to be Fast-Forward. Then the 'git rebase' command can help! 


Rebase

Copies commit on top of another branch without creating a commit, which keeps a linear history. Git will delete the old changes and rewrite them (with new sha1) on the top of the latest target (master) branch.


// Each branch has a new commit.

(master)

C1 -> C2

-> C3

(bugfix)


// After rebase, it turns into linear history

(master)

C1 -> C2

-> C2 -> C4 (rewritten commit from C3)

(bugfix)


NOTE:

1. Conflicts might be happened as usual, and we need to resolve them manually.

2. Rebase is not suitable if multiple people use the same branch.

3. The master branch is better not to do rebase

Below are the logs from my experiment (Rebasing without conflicts)


Step 1:


Check the status of the master branch


    $ git log
    commit cd1374abfde32ce13bc55f1abbb77571f230dedc (HEAD -> master)
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        second commit by master

    commit 54a804e5f7e75c74f5380cc097336957ca756f9f
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        first commit


Step 2:


Switch to the bugFix branch and check the status.


    git checkout bugFix
    Switched to branch 'bugFix'

    $ git log
    commit 38b42c91af547bd80692fae406ab6b325300b173 (HEAD -> bugFix)
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        first commit in bugFix

    commit 54a804e5f7e75c74f5380cc097336957ca756f9f
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        first commit


Step 3:


Check the git structure.


    $ tree .git/
    .git/
    ├── COMMIT_EDITMSG
    ├── HEAD
    ├── config
    ├── description
    ├── hooks
    ├── index
    ├── info
    │   └── exclude
    ├── logs
    │   ├── HEAD
    │   └── refs
    │       └── heads
    │           ├── bugfix
    │           └── master
    ├── objects
    │   ├── 18
    │   │   └── 0cf8328022becee9aaa2577a8f84ea2b9f3827
    │   ├── 34
    │   │   └── 84292db2aec3ad938ac17d3daf55dac8b03075
    │   ├── 38
    │   │   └── b42c91af547bd80692fae406ab6b325300b173
    │   ├── 54
    │   │   └── a804e5f7e75c74f5380cc097336957ca756f9f
    │   ├── 81
    │   │   └── fa9d1c5348f86326ce5e7a86d11b54c8140d8d
    │   ├── a5
    │   │   └── bce3fd2565d8f458555a0c6f42d0504a848bd5
    │   ├── b4
    │   │   └── 1fb42483f661a1de6202b2a872de08638acb9c
    │   ├── c0
    │   │   └── da834e42dcbf7b2b1c4a97925bef105d3863a3
    │   ├── cd
    │   │   └── 1374abfde32ce13bc55f1abbb77571f230dedc
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        │   ├── bugfix
        │   └── master
        └── tags

    20 directories, 20 files


Step 4:


Run the 'git rebase' command from master in the current bugFix branch.


    $ git rebase master
    First, rewinding head to replay your work on top of it...
    Applying: first commit in bugfix


 According to the message above, the commit 'first commit in bugfix' will be rewritten.


Step 5:


Check the status of the bugFix branch.

 
    $ git log
    commit 0238eb360ced00720a6efffbbc6c8f372eaf3c0d (HEAD -> bugFix)
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        first commit in bugFix

    commit cd1374abfde32ce13bc55f1abbb77571f230dedc (master)
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        second commit by master

    commit 54a804e5f7e75c74f5380cc097336957ca756f9f
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        first commit

1. Now, the new commit cd1374 from the master branch is copied to the bugfix branch.

2. You will notice that the sha1 of 'first commit in bugfix' is changed from '38b42c' to '0238eb'.


Step 6:


Compare the git structure.


    $ tree .git/
    .git/
    ├── COMMIT_EDITMSG
    ├── HEAD
    ├── ORIG_HEAD (NEW FILE)
    ├── config
    ├── description
    ├── hooks
    ├── index
    ├── info
    │   └── exclude
    ├── logs
    │   ├── HEAD
    │   └── refs
    │       └── heads
    │           ├── bugfix
    │           └── master
    ├── objects
    │   ├── 02 (NEW FOLDER)
    │   │   └── 38eb360ced00720a6efffbbc6c8f372eaf3c0d (NEW FILE, rewritten commit)
    │   ├── 18
    │   │   └── 0cf8328022becee9aaa2577a8f84ea2b9f3827
    │   ├── 34
    │   │   └── 84292db2aec3ad938ac17d3daf55dac8b03075
    │   ├── 38
    │   │   └── b42c91af547bd80692fae406ab6b325300b173
    │   ├── 54
    │   │   └── a804e5f7e75c74f5380cc097336957ca756f9f
    │   ├── 5a (NEW FOLDER)
    │   │   └── 3d043807c15183f3936b4e179acee9c0e2f26a (NEW FILE)
    │   ├── 81
    │   │   └── fa9d1c5348f86326ce5e7a86d11b54c8140d8d
    │   ├── a5
    │   │   └── bce3fd2565d8f458555a0c6f42d0504a848bd5
    │   ├── b4
    │   │   └── 1fb42483f661a1de6202b2a872de08638acb9c
    │   ├── c0
    │   │   └── da834e42dcbf7b2b1c4a97925bef105d3863a3
    │   ├── cd
    │   │   └── 1374abfde32ce13bc55f1abbb77571f230dedc
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        │   ├── bugfix
        │   └── master
        └── tags

    22 directories, 23 files

Check the rewritten commit.


    $ git cat-file -p 0238eb
    tree 5a3d043807c15183f3936b4e179acee9c0e2f26a
    parent cd1374abfde32ce13bc55f1abbb77571f230dedc
    author Frank <frank@demo.com>
    committer Frank <frank@demo.com>

        first commit in bugfix



    $ git cat-file -p 5a3d04
    100644 blob b41fb42483f661a1de6202b2a872de08638acb9c    test1.txt
    100644 blob 180cf8328022becee9aaa2577a8f84ea2b9f3827    test2.txt


Step 7:


Switch to the master branch and do the 'git merge' command.


    $ git checkout master
    Switched to branch 'master'



    $ git merge bugFix
    Updating cd1374a..0238eb3
    Fast-forward
    test2.txt | 1 +
    1 file changed, 1 insertion(+)
    create mode 100644 test2.txt


It is fast-forward, not 3 way merge

Thursday, November 12, 2020

git merge: 3-way merging


If it is not the linear path, then we need the 3-way merging.

First of all, create a new branch called bugFix from the master branch, and add a new commit called C4 to it.

---------------------------------------------------------------------------------

(master)

C1 -> C2 -> C3

-> C4

(bugFix branch)


Before merging the C4 commit to the bugFix branch, the master branch has added a new commit called C5. From the chart below, it is not the linear path anymore.

---------------------------------------------------------------------------------

(master) C1 -> C2 -> C3 -> C5 -> C4 (bugFix branch)


Use the 'git merge' command to merge the bugFix branch to the master branch.

---------------------------------------------------------------------------------

(master) C1 -> C2 -> C3 -> C5 -> C6 -> C4 -> (bugFix branch)


The 'git merge' command will help to create a new commit C6 and its parents are C4 and C5.

Below are the logs from my experiment (3-way merging without conflict)


Step 1:


Check the status of the bugFix branch.


    $ git log
    commit ec75b69d779c9f1d735f7aa1b7a551b9039eab6b (HEAD -> bugfix)
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        1 commit for bugFix branch

    commit b4c8993ddff375af46ccb0f2a632e172ffdf1bfc
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        1 commit for master


We notice that the bugFix branch pointed to the ec75b69d commit, but there is no info from the master branch in the git log.


Step 2:


Switch to the master branch and show the status.


    $ git checkout master

    $ git log
    commit d8563b55f6932b7819f4a6b107b8878ca7e73cff (HEAD -> master)
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        2 commit for master branch

    commit b4c8993ddff375af46ccb0f2a632e172ffdf1bfc
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        1 commit for master


Step 3:


Run the 'git merge' command to merge the bugFix branch to the master branch.


    $ git merge bugFix

    Then there is a prompt message:
    ----------------------------------------
    Merge branch 'bugFix'
    Please enter a commit message to explain why this merge is necessary, especially 
    if it merges an updated upstream into a topic branch.
    Lines starting with '#' will be ignored, and an empty message aborts the commit.
    ----------------------------------------

    // Message after saving the prompt message
    Merge made by the 'recursive' strategy.
    2.txt | 1 +
    1 file changed, 1 insertion(+)
    create mode 100644 2.txt

It is the auto-generated commit coming from the 'git merge' command.


Step 4:


Check the status.


    $ git log
    commit dc32187755d5bf601156db6e8e1cb6c9efd67f31 (HEAD -> master)
    Merge: d8563b5 ec75b69
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        Merge branch 'bugfix'

    commit d8563b55f6932b7819f4a6b107b8878ca7e73cff
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        2 commit for master branch

    commit ec75b69d779c9f1d735f7aa1b7a551b9039eab6b (bugfix)
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        1 commit for bugfix branch

    commit b4c8993ddff375af46ccb0f2a632e172ffdf1bfc
    Author: Frank <frank@demo.com>
    Date:   Timestamp

        1 commit for master


The commit dc3218 is the auto-generated commit by the 'git merge' command.

Check the detail for that commit.

    $ git cat-file -p dc3218
    tree 7eab585dfd08ca9f7524e71369236c074c9e4d93
    parent d8563b55f6932b7819f4a6b107b8878ca7e73cff
    parent ec75b69d779c9f1d735f7aa1b7a551b9039eab6b
    author Frank <frank@demo.com>
    committer Frank <frank@demo.com>

    Merge branch 'bugFix'

We notice that there are two parents in this commit object!!

Check the first parent info that shows that it is the latest commit from the master branch.


    $ git cat-file -p d8563b
    tree f6fa4e35a4c085d03ea91b14ba15f2ebfe4d09a7
    parent b4c8993ddff375af46ccb0f2a632e172ffdf1bfc
    author Frank <frank@demo.com>
    committer Frank <frank@demo.com>

    2 commit for master branch


Check the second parent info that shows that it is the latest commit from the bugFix branch.


    $ git cat-file -p ec75b69d7
    tree 434943a8265129a744745e5d12fa2625a784b283
    parent b4c8993ddff375af46ccb0f2a632e172ffdf1bfc
    author Frank <frank@demo.com>
    committer Frank <frank@demo.com>

    1 commit for bugfix branch


Wednesday, November 11, 2020

git merge: fast-forward


Fast-forward is the simplest scenario for 'git merge', but it is a rare case in reality.

The 'fast forward' required that there is no new commit in the 'master' branch.


For instance, in the beginning, the latest commit of the master branch is c3.

---------------------------------------------------------------------------------

(master branch)

C1  ->  C2  ->  C3


Then create a new branch called bugFix, and it points to the C3 commit.

---------------------------------------------------------------------------------

(master branch)

C1  ->  C2  ->  C3

(bugFix branch)


Then, later on, a new commit, c4, is created in the bugFix branch.

---------------------------------------------------------------------------------

(master branch)

C1  ->  C2  ->  C3

->  C4

(bugFix branch)


After fixing bugs in the bugFix branch, we want to merge our work to the master branch.

There is no new commit in the master branch, and it meets the 'fast forward' requirement.

Then it is the linear path.

---------------------------------------------------------------------------------

C1 ->   C2  ->  C3

->   C4

(bugFix branch)

(master branch)


If it is fast-forward case, then the 'master' branch pointer will move to the last commit of the 'bugfix' branch.


Step 1:


Create a new branch 'bugFix'


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

    $ git log
    commit bfcee5bf315aec756495f18ae100a1c7f0476cd0 (HEAD -> bugfixmaster)
    Author: Frank <frank@demo.com>
    Date:   timestamp

        first commit


Both the 'bugFix' and the 'master' branch point to the same commit 'bfcee5'.


Step 2:


Then create a new commit in the bugFix branch.


    $ echo 'test' > test.txt
    $ git add .
    $ git commit -m 'first commit from bugfix branch'
    [bugfix 2cb50e8] first commit from bugfix branch
    1 file changed, 1 insertion(+)
    create mode 100644 test.txt


 
    $ git log
    commit 2cb50e8d58b5fc5ab4f57efcd7b78d2231bb54e3 (HEAD -> bugfix)
    Author: Frank <frank@demo.com>
    Date:   timestamp

        first commit from bugfix branch

    commit bfcee5bf315aec756495f18ae100a1c7f0476cd0 (master)
    Author: Frank <frank@demo.com>
    Date:   timestamp

        first commit


Now, the bugfix branch is one commit ahead of the master branch.


Step 3:


Switch to the master branch and dump the logs


    $ git checkout master

    $ tree .git/
    .git/
    ├── COMMIT_EDITMSG
    ├── HEAD
    ├── config
    ├── description
    ├── hooks
    ├── index
    ├── info
    │   └── exclude
    ├── logs
    │   ├── HEAD
    │   └── refs
    │       └── heads
    │           ├── bugfix
    │           └── master
    ├── objects
    │   ├── 2c
    │   │   └── b50e8d58b5fc5ab4f57efcd7b78d2231bb54e3
    │   ├── 4d
    │   │   └── 37130db92414cbb28c58a1a0023f9ee23798e1
    │   ├── 9d
    │   │   └── aeafb9864cf43055ae93beb0afd6c7d144bfa4
    │   ├── aa
    │   │   └── a96ced2d9a1c8e72c56b253a0e2fe78393feb7
    │   ├── bf
    │   │   └── cee5bf315aec756495f18ae100a1c7f0476cd0
    │   ├── ce
    │   │   └── 013625030ba8dba906f756967f9e9ca394464a
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        │   ├── bugfix
        │   └── master
        └── tags

    17 directories, 17 files

    $ git log
    commit bfcee5bf315aec756495f18ae100a1c7f0476cd0 (HEAD -> master)
    Author: Frank <frank@demo.com>
    Date:   timestamp

        first commit


In the master branch, we don't know the new commits from the bugfix branch.


Step 4:


Run the 'git merge' command.


    $ git merge bugfix
    Updating bfcee5b..2cb50e8
    Fast-forward
    test.txt | 1 +
    1 file changed, 1 insertion(+)
    create mode 100644 test.txt



bfcee5b is the latest commit which the master branch pointed to before.
2cb50e8 is the latest commit of the bugfix branch. 
Also, the master branch pointed to this commit after merging.


Step 5:

Check the logs


    $ git log
    commit 2cb50e8d58b5fc5ab4f57efcd7b78d2231bb54e3 (HEAD -> master, bugfix)
    Author: Frank <frank@demo.com>
    Date:   timestamp

        first commit from bugfix branch

    commit bfcee5bf315aec756495f18ae100a1c7f0476cd0
    Author: Frank <frank@demo.com>
    Date:   timestamp

        first commit
  

After merging, both master and bugfix point to the same commit object.


    $ tree .git/
    .git/
    ├── COMMIT_EDITMSG
    ├── HEAD
    ├── ORIG_HEAD (NEW FILE)
    ├── config
    ├── description
    ├── hooks
    ├── index
    ├── info
    │   └── exclude
    ├── logs
    │   ├── HEAD
    │   └── refs
    │       └── heads
    │           ├── bugfix
    │           └── master
    ├── objects
    │   ├── 2c
    │   │   └── b50e8d58b5fc5ab4f57efcd7b78d2231bb54e3
    │   ├── 4d
    │   │   └── 37130db92414cbb28c58a1a0023f9ee23798e1
    │   ├── 9d
    │   │   └── aeafb9864cf43055ae93beb0afd6c7d144bfa4
    │   ├── aa
    │   │   └── a96ced2d9a1c8e72c56b253a0e2fe78393feb7
    │   ├── bf
    │   │   └── cee5bf315aec756495f18ae100a1c7f0476cd0
    │   ├── ce
    │   │   └── 013625030ba8dba906f756967f9e9ca394464a
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        │   ├── bugfix
        │   └── master
        └── tags

    17 directories, 18 files


There is a new file called 'ORIG_HEAD' under the .git folder.

It is used to revert the merge commit since git thinks that the 'merge command' is dangerous and easy to make a mistake.


    $ cat .git/refs/heads/master
    2cb50e8d58b5fc5ab4f57efcd7b78d2231bb54e3



'2cb50e' is the commit master branch points to now.
According to the results above, we prove the message 'Updating baab4d2..539e44c' is correct

Friday, June 26, 2020

SignalR and Angular


Currently, we want to keep our dashboard data updated through push notification instead of polling.
This note is written for how to implement it.
We will divide this note into two parts: One is for back-end service, and another one is for front-end app.

Back-end: SignalR Hub

1. First, we need to use Visual Studio to create a .Net Core 3.1 Web API project.

2. Then we need to install ‘SignalR’ via NgGet.


3. Now, we are able to create our hub class.

In this experiment, I created a class called ‘NotifyHub’ and inherit from 'Hub', and keep it empty.

    public class NotifyHub:Hub
    {
    
    }


4. Then I created a Controller to support Post Action for demo purpose instead of writing some code with timer to raise an event.

    [Route("api/[controller]")]
    [ApiController]
    public class NotificationController : ControllerBase
    {
        private IHubContext<NotifyHub> notifyHubContext;

        public NotificationController(IHubContext<NotifyHub> notifyHubContext)
        {
            this.notifyHubContext = notifyHubContext;
        }

        public IActionResult Post()
        {
            //Broadcast message to client  
            notifyHubContext.Clients.All.SendAsync("BroadcastMessage", "Hello World");

            return Ok();
        }
    }


5. In the end, we need to change the Startup.cs file for configuration.

Since our signalR hub project will have different origin comparing with angular client app, we need to config CORS. Below "htttp://localhost:4200" is for angular client.

    public void ConfigureServices(IServiceCollection services)
    { 
        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy", builder => builder
                .WithOrigins("http://localhost:4200")
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());
        });

        services.AddSignalR();

        services.AddControllers();
    }

Also, under ‘Configure’ function, add below code to apply CORS and hub.

    papp.UseCors("CorsPolicy");

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapHub<NotifyHub>("/notification");
    });

Finally, our SignalR back-end is ready.

Front-end: Angular Client App

1. First, we need to use Angular CLI to create a brand new project.

    ng new angular-client

2. Then, install signalr to this project

    npm install @aspnet/signalr --save

After that, you will see some changes in package.json file.

"@aspnet/signalr""^1.1.4",

3. Now, we are ready to use this library. In order to make it simple, we will not organize code structure. We put everything in app.component.ts for demo purpose.

    import { Component, OnInit } from '@angular/core';
    import * as signalR from "@aspnet/signalr";

    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    })
    export class AppComponent implements OnInit {
        // Variables
        private hubConnection: signalR.HubConnection

        ngOnInit(): void {
            // Binding
            this.hubConnection = new signalR.HubConnectionBuilder()
                .withUrl('https://localhost:5001/notification')
                .build();

            // Connect
            this.hubConnection
                .start()
                .then(
                    () => { } // Success
                )
                .catch(
                    err => { } // Failed
                )

            // Listening
            this.hubConnection.on('BroadcastMessage', (data) => {
                console.log(data);
            });
        }
    }


4. Once we send data through postman to our back-end Web API (https://localhost:5001/api/notification) , signalR hub server will push "Hello World' message to all its connected angular app.



 



And that is it.