Extreme Practices – Agile Tour Beirut

In my previous post, I shared with you how Philippe and I prepared for our talk “Extreme Practices.” In this post, I will be briefing the talk’s content; starting with the pitch and ending with the feedback! Philippe has already posted a blog on the talk that you can read here.

Pitching

dsc_0444-1

Each of the speakers had to brief their session in a thirty seconds pitch. This was mine:

This is unusual for me, because my co-presenter is in Paris! Philippe and I will demonstrate how we adopted the practices of Extreme Programming in our distributed team. We will also have two live demos; the first on remote pair programming and the second on remote meetings.

The talk

The audience started taking their seats; and in a couple of minutes, the room was full! We started by engaging the audience with three simple questions!

Who goes to work by car?

Who goes to work by bus?

Who goes to work on Skype?

Extreme programming

After introducing ourselves, our team and Murex, we spent the first half of the talk discussing four of the XP practices and their benefits.

  1. Ten Minutes Build
    • Helps developers stay focused on what they are doing
    • Shorten the feedback loop
    • Encourages developers to submit frequently thus resulting in easier bug analysis
  2. TDD
    • Only coding what makes tests pass decreases the possibility of generating bugs
    • In most cases, a failing unit test is enough to detect where the bug is and thus reducing the need for debugging
    • The refactoring step drives to clean code
    • Finding difficulty writing a test is an indication that refactoring is required
  3. Pair Programming
    • Benefits:
      • Newcomers tend to learn faster and submit on their first day
      • The quality of our code has increased
      • We didn’t notice any negative any impact on productivity
      • It helped us build a bonded team
    • Difficulties
      • It is very tiring for both the driver and navigator
      • It is risky because some developers prefer to work alone
  4. Retrospectives: For this part, we explained the five stages of our retrospectives
    • Check-in/energizer
    • Throwback
    • Collect insights & discuss
    • Actions
    • ROTI

Extreme programming in remote mode

Our second half of the talk was dedicated to sharing how we are applying XP in a remote mode, mainly focusing on Pair-Programing and Retrospectives. The discussion included the difficulties we faced at the beginning and how we managed to solve them. We ended the discussion on both topics by a live demo!

  1. Remote Pair Programming
    • To overcome the problem of time difference between the two cities, the pairs tend to share their calendars as well as an online document with the detailed tasks required to finish the story
    • The navigator might easily lose focus; that is why we try to submit frequently and switch control as much as possible
    • It is more tiring than local pair-programming especially if you have the headset on all day long. We agreed that anyone is free to ask for a break at any time
  2. Remote Retrospectives
    • The whiteBoards were located in Paris, and thus it was hard for us in Beirut to effectively contribute to the meetings. We managed to solve this problem by replacing our the whiteboard with an online Trello boards.
    • Initially, our meetings were held over the phone lacking any visualization of the team on the other side which caused a lot of frustration. To overcome this problem, our IT team installed Visio Conference rooms in both cities!

Here is a short video of the PairProgramming demo we did!

Main message

You don’t have to move abroad for your dream job!“.

Remote work is becoming the trend! The advancement of the collaboration tools and technologies is making it easier for companies to adopt. In the future, you will see more and more developers working from home.

That was our message to the audience!  We concluded that there are three ways to organize your team when working remotely:

  1. Split the team in two if there are enough members in each city
  2. Work in open-source mode if team members are distributed over many cities
  3. Finally, adopt our remote XP practices if it is not possible to split the team in two

Feedback

Kudo.png

In addition to the above two Kudo cards, I received several positive verbal feedback at the end of the session. All that was a sign that our talk was successful!

Slides

Finally, you can have a look on our slides here:

 

Extreme Practices – The Preparation

Extreme Practices was the name of the talk Philippe and I gave at the AgileTour in Beirut on 15th of October, which based on the feedback was a successful one! Our main focus was on the practices of extreme programming and how to adopt them in a distributed team.

As this was my first talk, I decided to write two blog posts about it. In this first blog, I will be sharing the preparation whereas in the second post (in few days) I will be talking about the talk itself.

It all started with a discussion

Earlier this year, I attended a workshop organized by Pierre Hervouet who is also the organizer of AgileTourBeirut. After the session, we had a lengthy discussion on how we are applying Extreme Programming in our distributed team. We ended that conversation by agreeing that I give a talk at the AgileTour on that subject.

The next day I discussed with Philippe the possibility of him being my co-speaker. Unfortunately, visiting Beirut during that time wasn’t possible for Philippe! But later it struck us; why not giving the presentation in a remote mode (i.e. I will be in Beirut while he is in Paris) to simulate how we work on a daily basis.

That kicked off the preparation for the talk!

The content

After a couple of brainstorming sessions, we defined our presentation’s content and agreed that having live demos of remote pair programming and remote retrospective would make the talk more valuable!

Here is the content we agreed on:

  1. Introduction of ourselves and our team
  2. A short definition of XP
  3. A detailed explanation of some XP practices (below) and how we are applying them
    1. 10 minutes build
    2. TDD (Test Driven Development)
    3. Pair Programming
    4. Retrospectives
  4. A short story on how we started the distributed team
  5. Remote pair programming
    1. Explain how we are doing it
    2. Discuss the difficulties we faced and how we managed to solve them
    3. Do a live demo and solve parts of the FizzBuzz problem
  6. Remote retrospectives
    1. A couple of stories on how we initially started doing remote meetings
    2. Again, mention all the difficulties and the respective solution
    3. Do a live demo
  7. Final message
  8. Answer questions

The next step was building up the presentation slides and preparing the demos!

Slides design

We are both software developers. Thus we have limited design skills plus we usually are busy at work and can’t spend a lot of time on the design. That is why we requested the help of our Internal Communications team in the Paris office! The team focused on enhancing the slides’ background and images, but the content was not modified. After a couple of iterations with them, we ended up with very well designed and beautiful slides!

The below images show a sample of the difference!

Presentation coach

I usually give talks, presentations and even training sessions at work, but this was my first attempt to give a talk at a conference. Yes, I was a bit worried about it! Thus, we asked for training with a professional coach from our training department in Paris.

We had two sessions with the trainer. The first one was in Paris (i.e. we were both in the same room) whereas the second one simulated the real scenario of the talk (i.e. I was in Beirut and Philippe in Paris).

The coach’s focus was on:

  1. The talk’s timing, to make sure that we don’t pass our allocated time. That led to the removal of some slides that were less relevance to the main message.
  2. The content, to make sure our message is well received by the audience. This led to the rewriting of parts of our text.
  3. Our presentation skills, which included how I stand on the stage, eye contact with the audience and Philippe’s intervention during the talk.

I have to say that this training was essential to the success of our talk! Some of the key points I learned from this training were:

  1. It is ok to forget and thus don’t hesitate to look at the notes if needed
  2. Limit the notes to headlines instead of full text
  3. Try not to look at the big screen
  4. Keep eye contact with all the audience

A big risk

Let’s admit, doing a remote presentation especially with an unstable connection as we have in Beirut is a huge risk! But we were well prepared!

Again here, I asked for help. But this time it was from the IT department at AUB (the tour’s host). They were very helpful, as they granted me a dedicated link with (relatively) high-speed Internet and performed two rehearsals to make sure everything is working as expected. (I took the below image from the last rehearsal)

To avoid any surprises during the talk, Philippe and I decided to record the two demos ahead of time and just play them if needed. Below you can check the two recorded demos.

Pair-programming recording

Retrospective recording

 

The preparation for the talk took a lot of time and was tiring, but it was worth as everything paid off at the end!

Stay tuned; next post is coming soon!

Coding dojo… One year later

Last week’s coding dojo session was a special one, not only because we brought a cake 🙂 but because it marked the first anniversary of those sessions at Murex Beirut. One year ago, I wrote a post sharing my experience on how we started this activity. Today, I am writing this post to share what has changed during this year.

Widening the scope

After twenty-eight sessions of practicing TDD, we decided it was time to adjust the scope a bit! Frequent attendees grasped TDD pretty well and suggested that we might benefit from those sessions to learn new technologies and practices.

After a brainstorming session, we agreed to label the session by one of four themes: Algorithms & TDD, New Languages, Kata & Workshops, and New Projects. We thought that by applying those themes we would increase the technical knowledge of all attendees, involve volunteers in the session preparation thus improving their presentations skills, and probably attract wider audience

Themes

Algorithms & TDD

Algorithms.png

We are all aligned that our primary purpose of this activity is practicing TDD, but in the case of complex algorithms using TDD is not always the optimal way (sudoku for example). Thus, we decided that in such cases we first would agree on the full algorithm then implement the code and write the sufficient tests to cover the different cases!

Lately, we’ve been using CodinGame to solve complex puzzles and algorithms, as the tests are already written, and they have an excellent graphical simulation of the puzzle execution.

New Languages

7Languages7Weeks

Learning new programming languages is one of the best approaches to improving someone’s programming skills; especially, when it includes learning different programming models (OO, functional, procedural, etc.) For those sessions, a volunteer learns the language alone and then presents it to the group in any form he/she prefers.

So far, we have learned two languages Scala and Ruby! For those sessions, we are using the book Seven Languages in Seven Weeks as our learning material reference and CodinGame as an exercising platform.

Kata & Workshops

JavaAgent.jpg

We defined kata as a presentation or short talk on a particular subject given by someone knowledgeable on that topic. A workshop, on the other hand, is a collaborative session where all take part of experimenting something new.

So far, we’ve had five of those sessions:

  1. A workshop on Git.
  2. Two kata sessions on machine learning.
  3. One presentation on Java agent and one on Android development.

Design patterns, development processes (Agile, lean, pair programming, etc.) and tools (docker, Gradle) are some of the candidates to be presented in next sessions.

New Projects

This is probably the hardest theme! The idea here is to implement an internal tool or app that can benefit other employees. Contributors to this theme will benefit from practicing TDD on a big scale project and learning new tools and technologies.

At this point, we are preparing a short list of proposals to get approval on one!  Hopefully, we can kickstart it soon 🙂

In Numbers

We initially started with thirty-four registered participants, but that number dropped to fourteen. Honestly, I think this isn’t a bad number!

The below graphs show the distribution of all the forty-seven sessions we’ve had so far. The graphs are grouped by theme and topic / language.

By Theme:
By Topic / Language:

More to Come

Thanks to the participants’ commitment and their eagerness to learn and improve their technical skills we celebrated the first anniversary of the coding dojo sessions. We will keep running those sessions, and we will keep improving them as we go! I hope that in one year from now, I will be posting another blog to share what we have done 🙂

Newcomers’ Training Program

Recently, I was in charge of training two fresh-graduate newcomers to our department. My mission was to prepare a two weeks program to ease the integration process with their teams.

After a short brainstorming, I decided to break the training into the following seven topics:

  1. Agile Practices: Their first assignment was getting acquainted with the Agile methodologies (mainly XP our working process). For that, I asked them to read a couple of chapters from two books “The Art of Agile Development” and “Extreme Programming Explained.”
  2. Dev Tools: Configuring some dev tools on their machines was the second step. This involved the installation and configuration of Java, IntelliJ, Maven and Perforce. Some of those tools such as Perforce and Maven were relatively new to them; so they took some time to learn more about it.
  3. TDDBy now, they were ready to write some code! And what would be better than following TDD to do that? Most of our teams started adopting TDD, thus coaching newcomers on TDD for simple dev problems is a must! For that purpose I picked the following two problems:
    • Mars Rover: This might be an easy problem, but I find it well suited to practice TDD especially for TDD newbies as it has a lot of cases to be covered by tests.
    • Coffee Machine: The beauty of this problem, is that it simulates what happens in the life cycle of an agile project, such as:
      • Defining new requirements at the start of each iteration
      • Writing the minimum code to implement the required features
      • Continuous code refactoring
      • Write the sufficient tests at each iteration
  4. Design Pattern and Code Refactoring: The two problems above may not be complex and can be solved in a short time, but the solution wasn’t the primary purpose rather it was introducing new concepts and practices to them. To make sure this purpose was achieved, I was performing multiple code review sessions during each iteration and suggesting enhancement at each time. This process elongated the time for each iteration, but it was worth. Some of the concepts I focused on were:
    • Test coverage
    • Builder pattern
    • Visitor pattern
    • Factory pattern
    • Bad and good code practices
    • Mocking
  5. Maven: They used Maven to build the code they wrote previously, but it was only maven’s basic commands. At this phase of the training, I asked them to dig deeper into maven to have a better understanding how it works; mainly focusing on:
    • Phases of build lifecycle
    • Dependency management
    • Plugins
    • Local and remote repositories
  6. SCM: Whether it is Git or Perforce, there are a couple of must know operations for any developer to be part of a development team. As a practice on those operations, they simulated a real dev cycle scenario by:
    • Sharing a common working directory on Perforce
    • Creating branches
    • Merging/Integrating changes
    • Resolving conflicts
  7. Continuous Integration (CI)As fresh graduates, the continuous integration was a new concept for them. Whereas for us, it is an essential process of our development cycle. It wasn’t possible to use an existing Jenkins instance to perform their testing; thus they executed the below steps:
    • Download and configure Jenkins locally on their machines
    • Submit their code to Perforce
    • Add a new job that syncs, compile code and execute the tests

 

I noticed the benefit of this training from the emails they sent me at the end of the program. They detailed what they learned and most importantly they were able to highlight the advantages of those practices and tools.

I hope you find this post helpful for your next newcomers’ training!

 

An XP Interview

While reading the book “Extreme Programming Explained,” I came across an interview with Brad Jensen a Senior Vice President of Airline Products Development at Sabre Airline Solutions. During the interview, Brad explained how he applied XP within his company and some of the difficulties he faced.

In this blog, I will be sharing my takeaways from this interview; because I thought it is worth sharing with others especially those willing to apply XP.

XPExplained

The Interview

Brad explained that he was able to apply XP in his company that consisted of 300 employees, 240 of which are developers, 25 in management and 35 in testing. His primary purpose was to bring 13 products into one organization with one architecture and one UI.

They started by giving a one-week training for each of the 13 groups then followed by coaching when they started applying XP. But, he advises on doing the training for one team at a time (making sure each time that the team got the concept of XP)

My Takeaways

  • How he made it work
    • XP was a perfect fit for Java projects with a motivated team
    • Testing and refactoring of C++ legacy code were very hard due to the complex design and lack of refactoring tools. Thus, XP had to be applied in a waterfall-ish way for such projects. Which meant:
      • more design and requirement gathering up-front
      • formal testing phase before deploying
    • Using XP decreased the number of defects to zero in some Java projects. For the C++ legacy, the defects dropped to a ratio of one to two per thousand lines of code
    • They noticed a 40% increase in productivity
    • It wasn’t easy to adopt XP! At first, only a third buy-in whereas the rest either are skeptical or just wait and see. Eventually, 80-90% buy-in, 10-20% use XP grudgingly and 3-5% never buy-in
    • If programmers don’t pair or insist on owning code have the courage to fire them
  • On-site Customers
    • A project manager should represent all of his customers
    • Having on-site customers is the most valuable part of XP because it gives you the ability to manage properly the scope and visibility on whether you are going to make it or not.
    • Without careful watching, on-site customers can cause the most problems as scope management can turn into scope creep
  • Advice for Executives
    • Plan by feature
    • Plan release once a quarter
    • Plan fixed-iterations more frequently
    • Have customers sit with the team
    • Put team in one open space

Give It a Try

In my team, we’ve been using this methodology for a while now, and it is working perfectly for us. We maintain clean code with test coverage up to 85%. We also managed to tune it to fit our needs, for example doing remote pair programming (you can check some of Philippe’s posts for more insights on the way we work.)

The importance of this interview is that it provided a real example of applying XP not only theories. Thus, it might encourage leaders to give XP a try!

I highly advise you to read this book. If you are new to XP, this book will give you insights on what this methodology is, why is it important and how to adopt it within your team. If you are already an XP developer, this book is your reference to know whether you are applying it correctly or not.

” You will only value XP when you give it a try!”

 

Using H2 In-Memory to test your DAL

How should we test the Data Access Layer code?

Many developers ask that question. Similar to the other layers of your system, it should be fully tested to prevent unexpected and random behavior in production. There are many ways to achieve that, among of which are mocks or in-memory database.

The problem with mocks is that instead of testing the validity of your SQL queries (syntax and execution), you will only be testing the validity of the system’s flow. On the other hand, using the in-memory database will validate both! That is why I prefer it over mocking. But, you should keep in mind that the SQL syntax might differ from one database engine to another.

In this blog, I will be giving an example of using “H2 In-Memory” in unit tests. The code of this example is available on my GitHub account.

H2 In-Memory in Action

So, let us see H2 in action 🙂

For the sake of this blog, I will assume we want to test three SQL operations on the table “Members” having the following model:

MEMBERS
ID      | NAME        |
INTEGER | VARCHAR(64) |

The SQL operations to be covered in this blog are:

  1. Create Table
  2. Insert (batch of prepared statements)
  3. Select *

Code Explanation

The example will be based on five files (pom.xml, Member.java, SqlRepository.java, H2Repository.java and H2MembersRepositoryTest). In this section, I will give a brief explanation of each file.

Pom.xml (Maven Dependencies):

First, let us modify our pom file (as shown below) to make our project depend on two projects:

  1. com.h2database: Using this dependency, Maven will take care of downloading the h2 jar file we will be referencing in our tests.
  2. Junit: a unit testing framework for Java
<dependencies>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.191</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
</dependencies>

Member.java:

This class represents a Member record. It consists of a factory method that returns an instance of the Member class and two getter methods to return the values of the Id and Name fields.

package dal;

import java.util.Objects;

public final class Member {
    private final int id;
    private final String name;

    private Member(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static Member aMember(int id, String name) {
        return new Member(id, name);
    }

    public int id() {
        return id;
    }

    public String name() {
        return name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Member member = (Member) o;
        return id == member.id &&
                Objects.equals(name, member.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

SqlRepository.java

In this class, we establish a connection to our database. What is important in this class is that we pass the connection string as a parameter to the constructor thus making our code unbounded to any specific database engine (MySql, H2, Sybase, Oracle, etc.). This will make writing our tests much easier!

The class consists of:

  1. Constructor: initializes an instance of SQL Connection using the connection string passed as parameter
  2. Three public methods:
    1. createTable: executes an update query to create the”MEMBERS” table.
    2. allMemebers: executes a select query and returns the found records in a list of Members.
    3. insertMembers: takes a list of “Member” as a parameter and inserts the values into the “MEMBERS” table.
package dal;

import dal.Member;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SqlRepository {

    protected final Connection connection;
    private static final String CREATE_MEMBERS = "CREATE TABLE MEMBERS(ID INTEGER, NAME VARCHAR(64))";
    private static final String SELECT_MEMBERS = "SELECT * FROM MEMBERS";
    private static final String INSERT_MEMBERS = "INSERT INTO MEMBERS(ID, NAME) VALUES(?, ?)";

    public SqlRepository(String connectionString) throws SQLException {
        connection = DriverManager.getConnection(connectionString);
    }

    public boolean createTable() throws SQLException {
        Statement createStatement = connection.createStatement();
        return createStatement.execute(CREATE_MEMBERS);
    }

    public List<Member> allMembers() throws SQLException {
        List<Member> allMembers = new ArrayList<>();
        Statement selectStatement = connection.createStatement();
        ResultSet membersResultSet = selectStatement.executeQuery(SELECT_MEMBERS);
        while (membersResultSet.next()) {
            allMembers.add(Member.aMember(membersResultSet.getInt(1), membersResultSet.getString(2)));
        }
        return allMembers;
    }

    public void insertMembers(List<Member> members) throws SQLException {
        final PreparedStatement insertMembers = connection.prepareStatement(INSERT_MEMBERS);
        members.forEach(member -> insertMember(member, insertMembers));
        insertMembers.executeBatch();
    }

    private void insertMember(Member member, PreparedStatement insertMembers) {
        try {
            insertMembers.setInt(1, member.id());
            insertMembers.setString(2, member.name());
            insertMembers.addBatch();
        } catch (SQLException e) {
            throw new UnsupportedOperationException(e.getMessage());
        }
    }
}

H2Repository.java

I added this class under “Test Sources Root” because it is only used by the tests.

As you notice, it extends the SqlRepository class implemented previously. Thus, we don’t have a lot to implement here. The only method added is a new method “closeConnection” that drops all the existing tables from the database.

You might wonder why would we need that since we are using an In-Memory database. That might be true for this simple example, but it will be a necessity when running multiple tests classes. That is because, in Java, all the tests are run in the same JVM which means that the H2 instance initialized in the first test will be shared with the next test classes. This approach might lead to an unexpected behavior when using the same tables in the different test classes.

package dal;

import java.sql.SQLException;

public final class H2Repository extends SqlRepository {
    public H2Repository(String connectionString) throws SQLException {
        super(connectionString);
    }

    public void closeConnection() throws SQLException {
        connection.createStatement().execute("DROP ALL OBJECTS");
    }
}

H2MembersRepositoryTest.java

This is the simple test class! Our single test (it_correctly_inserts_members_to_a_database) is invoking the three methods we implemented before (createTable, insertMember and allMembers). If any of those methods is badly written our test would fail.

package dal;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import static dal.Member.aMember;
import static org.fest.assertions.Assertions.assertThat;

public class H2MembersRepositoryTest {

    private static final String H2_CONNECTION_STRING = "jdbc:h2:mem:test";
    private static final List<Member> MEMBERS = new ArrayList<>(10);
    private static H2Repository h2Repository;

    @BeforeClass
    public static void
    setup_database() throws SQLException {
        h2Repository = new H2Repository(H2_CONNECTION_STRING);
        initializeMembers();
    }

    @Test
    public void
    it_correctly_inserts_members_to_a_database() throws SQLException {
        h2Repository.createTable();
        h2Repository.insertMembers(MEMBERS);

        assertThat(h2Repository.allMembers()).isEqualTo(MEMBERS);
    }

    private static void initializeMembers() {
        for (int index = 0; index < 10; index++) {
            MEMBERS.add(aMember(index, "Name_" + index));
        }
    }

    @AfterClass
    public static void
    tear_down_database() throws SQLException {
        h2Repository.closeConnection();
    }
}

TearDown

If you can test your DAL, you can test anything! (I just came up with this ;))

Keep the tests going!

References:

  1. H2-Database Engine
  2. H2-Maven
  3. JUnit Maven
  4. GitHub

How to move a git repository to subdirectory of another repository

You might come across a situation when working with Git where you want to move a repository to another repository (i.e., make it a subdirectory of the other` repository) while maintaining its full history.

In this blog, I will be pointing out step by step with an example how to achieve that.

Use Case

Let us assume we have two repositories on git, repoA, and repoB, and we want to make repoB a subdirectory of repoA while maintaining its full history.

To achieve that we will move the content of repoB into a subfolder under repoB and then merge repoA and repoB. 

Bellow is the detailed git process:

  1. The first step is to clone repoA and repoB on your machine
    $ git clone repoA
    $ git clone repoB

    Note: For the purpose of this blog I submitted a README.md file in each of the two repositories on Bitbucket. Below are two snapshots are taken for both repositories:

    RepoA:
    REPO-A-Commits

    RepoB:
    REPO-B-Commits

  2. The second step is to copy the content of repoB to a subfolder. Go to the folder repoB and apply the following steps:
    • create a subfolder repoB
      $ mkdir repoB
    • move everything from the parent repoB to the child repoB (except the .git folder)
    • stage the files (the added folder and deleted file(s)) for a later commit
      $ git stage repoB/
      $ git stage README.md
    • commit and push those changes to git
      $ git commit -am '[REPO-B] Move content to a subfolder'
      $ git push origin master
  3. Go to the folder repoA and do the following:
    • add a remote branch with the content of repoB
      $ git remote add repoBTemp (path_to_repoB)
    • fetch repoBTemp the temp repo we created in the previous step
      $ git fetch repoBTemp
    • merge repoBTemp with repoA
      $ git merge repoBTemp/master
    • delete the remote repoBTemp
      $ git remote rm repoBTemp
    • push the changes to the server

      $ git push origin master

  4. Now we can check our git history to verify that our trick worked as intended. In the right image below you can see that the history of repoB is now part of repoA’s history. In the left image, you can notice that repoB is now a subdirectory of repoA.
  5. You it should be sage to delete the repository repoB

I hope you find this blog helpful!