Java 2

Week 9

  • Sorting and Filtering Lists
  • Functional Interfaces
  • Lambda Expressions, Method References
  • Collaborative GitHub

Comparable Interface

  • Update the Movie class to implement the Comparable interface to create a natural sort order.

  • Because movie IDs are strings we can compare two Strings using the provided compareTo or compareToIgnoreCase method.

  • Generate an equals and hashCode method.

package edu.kirkwood.model;

import java.util.Comparator;
import java.util.Objects;

public class Movie implements Comparable<Movie> {
    // existing code

	/**
     * Compares objects by their id (natural sort order)
     */
	@Override
    public int compareTo(Movie o) {
        // The String class has its own compareTo and compareToIgnoreCase method.
        return this.id.compareTo(o.id);
    }
    
    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;
        Movie movie = (Movie) o;
        return year == movie.year && Objects.equals(id, movie.id) && Objects.equals(title, movie.title) && Objects.equals(plot, movie.plot);
    }

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

}

Unit Tests

  • Create a MovieTest class.

  • In IntelliJ, click the Fix button to add the JUnit dependency to the pom.xml file.

  • Create a setUp method to instantiate a couple of mock objects.

package edu.kirkwood.model;

import org.junit.jupiter.api.BeforeEach;

import java.util.ArrayList;
import java.util.List;

class MovieTest {
    private Movie m1;
    private Movie m2;
    private Movie m3;
    private List<Movie> movies;

    @BeforeEach
    void setUp() {
        m1 = new Movie("110", "C", 2025, "No Plot");
        m2 = new Movie("11", "a", 2025, "No Plot");
        m3 = new Movie("11", "B", 2025, "No Plot");
        movies = new ArrayList<>();
        movies.add(m1);
        movies.add(m2);
        movies.add(m3);
    }

}

Unit Tests

  • Write unit tests to verify the compareTo method.

  • It will return a negative number if the first object comes before the second object. It will return 0 if the two objects are the same. It will return a positive number if the first object comes after the second object.

  • The tests will fail because "101" comes alphabetically after "11"

package edu.kirkwood.model;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

class MovieTest {
    private Movie m1;
    private Movie m2;
    private Movie m3;
    private List<Movie> movies;

    @BeforeEach
    void setUp() {
        m1 = new Movie("110", "C", 2025, "No Plot");
        m2 = new Movie("11", "a", 2025, "No Plot");
        m3 = new Movie("11", "B", 2025, "No Plot");
        movies = new ArrayList<>();
        movies.add(m1);
        movies.add(m2);
        movies.add(m3);
    }

    @Test
    void compareToNegative() {
        // Arrange
        int expected = -1; // A negative number means obj1 comes before obj2
        // Act
        int actual = m2.compareTo(m1);
        // Assert
        assertEquals(expected, actual);
    }

    @Test
    void compareToZero() {
        // Arrange
        int expected = 0; // Zero means obj1 and obj2 are the same
        // Act
        int actual = m2.compareTo(m3);
        // Assert
        assertEquals(expected, actual);
    }

    @Test
    void compareToPositve() {
        // Arrange
        int expected = 1; // Positive means obj1 comes after obj2
        // Act
        int actual = m1.compareTo(m3);
        // Assert
        assertEquals(expected, actual);
    }
}

Unit Tests

  • Update the compareTo method to first sort by the length of the id.

  • If the first id length is 2 and the second is 3, this will return -1

  • If the first id length is 2 and the second is 2, this will return 0

  • If the first id length is 3 and the second is 2, this will return 1.

  • If you run the unit tests, they will pass. However, if you increase one movie id length, the test will fail because it is now returning -2 or 2, not -1 or 1.

/**
 * Compares objects by their id (natural sort order)
 */
 @Override
 public int compareTo(Movie o) {
    // First sort by the length of the id
    if(this.id.length() != o.id.length()) {
    	return this.id.length() - o.id.length()
    }
    // If lengths are the same sort them alphabetically
    // The String class has its own compareTo and compareToIgnoreCase method.
    return this.id.compareTo(o.id);
 }

Unit Tests

  • The Integer class has a static compare method.

  • If the first id length is smaller than the second, this will return -1

  • If the first id length is the same as the second, this will return 0

  • If the first id length is larger than the second, this will return 1.

/**
 * Compares objects by their id (natural sort order)
 */
 @Override
 public int compareTo(Movie o) {
    // First sort by the length of the id
    if(this.id.length() != o.id.length()) {
    	return Integer.compare(this.id.length(), o.id.length());
    }
    // If lengths are the same sort them alphabetically
    // The String class has its own compareTo and compareToIgnoreCase method.
    return this.id.compareTo(o.id);
 }

Comparator Functional Interface

  • Create Comparator objects to compare by other data.

  • To create a lambda, start with a pair of parenthesis.
  • Inside the parenthesis you must define the functional interface's abstract method's parameters.
    • To sort things, we need to compare two objects. Those will be our parameters.
  • After the parenthesis type an arrow/lambda operator -> (a dash and greater than sign), followed by a set of curly brackets.
  • Press enter between the curly brackets. The last line will be });
  • Type code in between the curly brackets.
package edu.kirkwood.model;

import java.util.Comparator;
import java.util.Objects;

public class Movie implements Comparable<Movie> {
    // existing code

	@Override
    public int compareTo(Movie o) {
        // Compares objects by their id (natural sort order)
        // The String class has its own compareTo and compareToIgnoreCase method.
        if(this.id.length() != o.id.length()) {
            return this.id.length() - o.id.length();
        }
        return this.id.compareTo(o.id);
    }

    // Compare objects by their title (A to Z)
    public static Comparator<Movie> compareTitleLambdaLong = (Movie m1, Movie m2) -> {
        return m1.getTitle().compareTo(m2.getTitle());
    };


}

Comparator Functional Interface

  • When you declare Comparator<Movie>, Java knows that the two input parameters are Movies, so you can just type (m1, m2).

  • If the code between the curly brackets is a single return statement, the curly brackets and return keyword can be omitted.
  • Written in short form, IntelliJ will suggest that you substitute the code using a method reference.
    • In order to work, the method reference being called must have the same input parameters and return value as defined in the functional interface's abstract method.
  • Create additional Comparator objects.
package edu.kirkwood.model;

import java.util.Comparator;
import java.util.Objects;

public class Movie implements Comparable<Movie> {
    // existing code

	@Override
    public int compareTo(Movie o) {
        // Compares objects by their id (natural sort order)
        // The String class has its own compareTo and compareToIgnoreCase method.
        if(this.id.length() != o.id.length()) {
            return this.id.length() - o.id.length();
        }
        return this.id.compareTo(o.id);
    }

    // Compare objects by their title (A to Z)
    public static Comparator<Movie> compareTitleLambdaLong = (Movie m1, Movie m2) -> {
        return m1.getTitle().compareTo(m2.getTitle());
    };
    public static Comparator<Movie> compareTitleLambdaShort = (m1, m2) -> m1.getTitle().compareTo(m2.getTitle());
    public static Comparator<Movie> compareTitle = Comparator.comparing(Movie::getTitle).thenComparing(Movie::getId);
    // Compares objects by their year (oldest to newest)
    public static Comparator<Movie> compareYear = Comparator.comparing(Movie::getYear).thenComparing(Movie::getId);

}

PresidentDAO Class

  • Write code to get the list of President objects, then sort and display the data.

import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;

public class PresidentDAO {
    private static List<President> presidents = new ArrayList<>();
    
    public static void main(String[] args) {
    	Collections.sort(list);
        printList("Alphabetical A-Z", list);

        Collections.reverse(list);
        printList("Alphabetical Z-A", list);

        // Collections.sort(list,  President.compareHeight);
        // printList("Shortest 5", list, 5);

        // Collections.sort(list,  President.compareHeight.reversed());
        // printList("Tallest 5", list, 5);

        // printOneWithLabel("Shortest", list.stream().min(President.compareHeight).get());

        // printOneWithLabel("Tallest", list.stream().max(President.compareHeight).get());

        // Collections.sort(list, President.compareWeight);
        // printList("Lightest 5", list, 5);

        // Collections.sort(list, President.compareWeight.reversed());
        // printList("Heaviest 5", list, 5);

        // printOneWithLabel("Lightest", list.stream().min(President.compareWeight).get());

        // printOneWithLabel("Heaviest", list.stream().max(President.compareWeight).get());

        // Collections.sort(list, President.compareAge);
        // printList("Oldest 5", list, 5);

        // Collections.sort(list, President.compareAge.reversed());
        // printList("Youngest 5", list, 5);

        // printOneWithLabel("Youngest", list.stream().min(President.compareAge).get());

        // printOneWithLabel("Oldest", list.stream().max(President.compareAge).get());
    }
    
    public static void printList(String title, List<President> presidents) {
        printList(title, presidents, presidents.size());
    }

    public static void printList(String title, List<President> presidents, int count) {
        // The list is printed, formatted as a numbered list, with a title above it.
        System.out.println("---------" + title + "---------");
        for(int i = 0; i < count; i++) {
            System.out.printf("%s) %s, %s\n", (i + 1), presidents.get(i).getLastName(), presidents.get(i).getFirstName());
        }
        System.out.println();
    }

    public static void printOneWithLabel(String label, President president) {
        // Prints a record with a label in front
        System.out.printf("%s: %s, %s\n\n", label, president.getLastName(), president.getFirstName());
    }

    public static List<President> getPresidents() {
        if(presidents.size() == 0) {
             getFromCSV();
        }
        return presidents;
    }

    private static void getFromCSV() {
        List<String> lines = FileInput.readAllLines("presidents.csv");
        for(String line: lines) {
            String[] president = line.split(",");
            try {
                int id = Integer.parseInt(president[0].trim());
                String firstName = president[1].trim();
                String lastName = president[2].trim();
                int height = Integer.parseInt(president[3].trim());
                double weight = Double.parseDouble(president[4].trim());
                LocalDate dateOfBirth = LocalDate.parse(president[5].trim());
                presidents.add(new President(id, firstName, lastName, height, weight, dateOfBirth));
            } catch(IndexOutOfBoundsException | NumberFormatException | DateTimeParseException e) {
                // Skip the line if it is missing a field, or if the String cannot be converted into an int, double, or LocalDate.
                continue;
            }
        }
    }
    
}

Lambdas in Long Form

  • Remove the Comparable implementation in the Book class.
  •  
public class Main {

    public static void usingLambdasLong() {
    	List<Book> books = Books.all();
        Collections.sort(books, (Book b1, Book b2) -> {
            return b1.getTitle().compareTo(b2.getTitle());
        });
        
        for(Book book: books) {
        	System.out.println(book);
        }
    }
    
    public static void main(String[] args) {
        usingLambdasLong();
    }
    
}

The Declarative Way

  • Let's modify the method into a short declarative form.
  • The compiler can figure stuff out well enough that you don't need to declare types. Remove references to Book.
  • The single line shown below is called an expression type body.
    • It creates a function that takes two book objects, and returns the value from the compareTo statement.
public class Main {

    public static void usingLambdasShort() {
    	List<Book> books = Books.all();
        Collections.sort(books, (b1, b2) -> b1.getTitle().compareTo(b2.getTitle()));
        
        for(Book book: books) {
            System.out.println(book);
        }
    }
    
    public static void main(String[] args) {
        usingLambdasShort();
    }
    
}

Method References

  • We use the getTitle method on each instance of Book to get the value that will be used for the comparing command.
  • The lambda in the .forEach() method can use the static method System.out and then the method we want to do, which is println.
    • This returns a reference to the println method and for each applies the entry as the parameter to the function.
  • Another sort example is shown using a List of Strings.
public class Main {

    public static void usingMethodReferences() {
    	List<Book> books = Books.all();
        // Collections.sort(books, Comparator.comparing(b -> b.getTitle()));
        Collections.sort(books, Comparator.comparing(Book::getTitle));
        books.forEach(System.out::println);
    }
    
    public static void main(String[] args) {
        usingMethodReferences();
        
        List<String> names = new ArrayList<>();
        names.add("Marc");
        names.add("amy");
        
        Collections.sort(names, String::compareToIgnoreCase);
        
        names.forEach(name -> {
            name = name.toUpperCase();
            System.out.printf("Hello %s\n", name);
        });
    }
    
}

Animation

  • Create a view class called Animator that will take a message String, like "Loading", and animate an ellipsis to be used when the program loads data.

  • Implement the Runnable interface and override the run method.

package edu.kirkwood.view;

/**
 * A Runnable class that handles the animation logic. This will be run on a
 * separate thread.
 */
public class Animator implements Runnable {
    private final String message;

    /**
     * Constructs the Animator with a message to display.
     * @param message The base text for the loading message.
     */
    public Animator(String message) {
        this.message = message;
    }

    @Override
    public void run() {
        
    }
}

Multithreading

  • Define a boolean flag variable called "running".

  • Use 'volatile' to ensure it is always read from main memory.

    • This is important for thread safety when one thread modifies the flag and another reads it.

  • Define a method to stop the animation manually.

package edu.kirkwood.view;

/**
 * A Runnable class that handles the animation logic. This will be run on a
 * separate thread.
 */
public class Animator implements Runnable {
    private volatile boolean running = true;
    private final String message;

    /**
     * Constructs the Animator with a message to display.
     * @param message The base text for the loading message.
     */
    public Animator(String message) {
        this.message = message;
    }

    /**
     * Signals the animation thread to stop after its current cycle.
     */
    public void stopAnimation() {
        this.running = false;
    }

    @Override
    public void run() {
        while (running) {
            
        }

    }
}

Multithreading

  • Create a loop inside the run method that runs indefinitely.

  • Call Thread.sleep() to pause the thread for a short duration to control the animation speed.

    • 400 means 400 milliseconds, or 0.4 second.

  • When the thread is interrupted, restore the interrupted status and stop running the loop.

package edu.kirkwood.view;

/**
 * A Runnable class that handles the animation logic. This will be run on a
 * separate thread.
 */
public class Animator implements Runnable {
    private volatile boolean running = true;
    private final String message;

    /**
     * Constructs the Animator with a message to display.
     * @param message The base text for the loading message.
     */
    public Animator(String message) {
        this.message = message;
    }

    /**
     * Signals the animation thread to stop after its current cycle.
     */
    public void stopAnimation() {
        this.running = false;
    }

    @Override
    public void run() {
        while (running) {
            

            try {
                // Pause the thread for a short duration to control the animation speed.
                Thread.sleep(400);
            } catch (InterruptedException e) {
                // If the thread is interrupted, restore the interrupted status and stop running.
                Thread.currentThread().interrupt();
                running = false;
            }
        }

    }
}

Animation States

  • Add a states String array containing three animation states

  • Add a currentStateIndex variable to keep track of the current state

  • Inside the loop, print the message followed by the current animation state.

    • The carriage return '\r' moves the cursor to the beginning of the line so we can overwrite it in the next frame of the animation.

package edu.kirkwood.view;

/**
 * A Runnable class that handles the animation logic. This will be run on a
 * separate thread.
 */
public class Animator implements Runnable {
    // Use 'volatile' to ensure the 'running' flag is always read from main memory.
    // This is crucial for thread safety when one thread modifies the flag and another reads it.
    private volatile boolean running = true;
    private final String message;
    private final String[] states = {".  ", ".. ", "..."};
    private int currentStateIndex = 0;

    /**
     * Constructs the Animator with a message to display.
     * @param message The base text for the loading message.
     */
    public Animator(String message) {
        this.message = message;
    }

    /**
     * Signals the animation thread to stop after its current cycle.
     */
    public void stopAnimation() {
        this.running = false;
    }

    @Override
    public void run() {
        while (running) {
            System.out.print("\r" + message + states[currentStateIndex]);

            try {
                Thread.sleep(400);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                running = false;
            }
        }
        
    }
}

Animation States

  • Cycle to the next state. The modulo operator ensures it wraps around.

  • After the loop finishes, clear the line.

  • We print spaces to overwrite the loading message completely.

package edu.kirkwood.view;

/**
 * A Runnable class that handles the animation logic. This will be run on a
 * separate thread.
 */
public class Animator implements Runnable {
    // Use 'volatile' to ensure the 'running' flag is always read from main memory.
    // This is crucial for thread safety when one thread modifies the flag and another reads it.
    private volatile boolean running = true;
    private final String message;
    private final String[] states = {".  ", ".. ", "..."};
    private int currentStateIndex = 0;

    /**
     * Constructs the Animator with a message to display.
     * @param message The base text for the loading message.
     */
    public Animator(String message) {
        this.message = message;
    }

    /**
     * Signals the animation thread to stop after its current cycle.
     */
    public void stopAnimation() {
        this.running = false;
    }

    @Override
    public void run() {
        while (running) {
            System.out.print("\r" + message + states[currentStateIndex]);

            currentStateIndex = (currentStateIndex + 1) % states.length;

            try {
                Thread.sleep(400);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                running = false;
            }
        }

        String cleanup = " ".repeat(message.length() + 3);
        System.out.print("\r" + cleanup + "\r");
    }
}

Helpers

  • Inside the Helpers class, create an overloaded method called printList that takes a String title, List<T>, and an optional count. 

  • Type <T> means the input can be any type of List, such as List<Movie>, List<Fraction>, List<String> etc.

  • Whenever a method references Type <T>, you must include <T> before the return type.

  • Create a loop that prints each item with a number in front.

public class Helpers {
    public static <T> void printList(String title, List<T> list) {
        printList(title, list, list.size());
    }

    public static <T> void printList(String title, List<T> list, int count) {
        // The list is printed, formatted as a numbered list, with a title above it.
        System.out.println("---------" + title + "---------");
        for(int i = 0; i < count; i++) {
            System.out.printf("%s) %s\n", (i + 1), list.get(i));
        }
        System.out.println();
    }

	// existing code
}

Helpers

  • Create another generic method called printOneWithLabel that takes a String title, and T obj. 

  • Type T means the input can be any object, such as Movie, Fraction, String, etc.

  • Whenever a method references Type T, you must include <T> before the return type.

public class Helpers {
    public static <T> void printList(String title, List<T> list) {
        printList(title, list, list.size());
    }

    public static <T> void printList(String title, List<T> list, int count) {
        // The list is printed, formatted as a numbered list, with a title above it.
        System.out.println("---------" + title + "---------");
        for(int i = 0; i < count; i++) {
            System.out.printf("%s) %s\n", (i + 1), list.get(i));
        }
        System.out.println();
    }

    public static <T> void printOneWithLabel(String title, T obj) {
        // Prints a record with a label in front
        System.out.println("---------" + title + "---------");
        System.out.println(obj);
    }

	// existing code
}

DataApp

  • Call the UserInput.getString() method to prompt the user for a movie title.

  • Create a getResults method that takes the movie title and returns a List<Movie>.

public class MyDataApp {
    public static void main(String[] args) throws InterruptedException {
        String title = getString("Enter a movie title", true);

        List<Movie> results = getResults(title);
    }

    public static List<Movie> getResults(String title) {
        

        return List.of();
    }
}

Hacktoberfest

Project Setup

  • The instructor (aka the maintainer) has a public repository for the Calculators project.
  • Students (aka contributors) must fork the project.
    • A fork is a copy of the maintainer's repository, allowing contributors to make changes.
  • Close any open projects in IntelliJ.
  • In IntelliJ, contributors must click the "Get from VCS" button to clone their forked copy to their computer.
    • Cloning is the process of making a local copy of a project to your local machine.
    • You may get a notification saying you need to install the Java Development Kit (JDK) chosen by the maintainer.

Origin vs Upstream

  • Contributors will open the Terminal in IntelliJ.
  • Enter this command to view the list of remote repositories. Your forked repository will display as "origin".
    git remote -v
    • Origin is a reference to your forked project.
  • Enter this command to specify the upstream repository that will be synced with the fork. Replace the x's with the repo path.
    git remote add upstream https://github.com/mlhaus/xxxx.git
    • Upstream is a reference to the maintainer's project.
  • Enter this command again to confirm that the new upstream repository you've specified.
    git remote -v

Step 1: Create a GitHub Issue

  • On the maintainer's repository, create a new Issue. 
  • Explain a feature you want to add or a bug you want to fix. 
  • Have a discussion with the maintainer to ensure your ideas are harmonious and don't conflict with other's work.

Step 2: Create a New Branch

  • Use the Git > Branches command to create and checkout a new branch called "yourname-contribution1".
  • Or they can enter this command:
    git checkout -b yourname-contribution1

Step 3: Add New Code

  • The work done on a new branch does not affect the main branch.
    • This is particularly useful when developing features or trying out new ideas.
    • When working collaboratively, never work on the main branch!
  • Add new packages and files on the new branch.
    • Create a package called "edu.kirkwood.model.yourlastname". Inside this package, add your model class. 
    • Create a Test class. Inside this class, add your model unit tests.
  • Please do not edit or delete any existing files. Ensure you are not duplicating the work of another developer. These things will later cause merge conflicts, which is a topic for a different lesson.

Step 4: Commit and Push

  • Use the Git menu to commit and push the changes.
    • For now, only add .java files, no .idea files like workspace.xml
    • Make sure the commit message is meaningful and concise.
    • Ensure you are pushing the "yourname-contribution1" branch, not the "main" branch.
  • Or you can enter these commands:
    git add .
    git commit -m 'A meaningful message'

    git push origin yourname-contribution1

Step 5: Make a Pull Request

  • Go to the "origin" GitHub repository (your forked copy).
  • A message will appear in a yellow box saying "yourname-contribution1 had recent pushes".
  • Click the green "Compare & Pull request" button.
  • Scroll down the page and inspect the changes made (green means added, red means deleted).
  • Click the green "Create pull request" button.
  • GitHub will redirect you to the "upstream" GitHub repository (the maintainer's original work).
  • Wait until the maintainer approves and merges the pull request. Remote branches can be deleted after they are merged.

Step 6: Update Main Branch

  •  
  • Update the project by pulling changes from origin.
    • Or you can enter this command:
      git pull origin main
  • All files will be merged into the main branch.
  • You can safely delete the local "yourname-contribution1" branch.

Step 7: Fetch Changes

  • Back in IntelliJ, switch back to the main branch.
    • Or you can enter this command: git checkout main
  • It will appear that the new work is gone. Don't worry, it's on a different branch.
  • Note: If you make any changes to the main branch, you must remove or commit the code before continuing.

  • Use the Git > Fetch command to obtain the change history (branches and commits) from the upstream repo.

    • Or you can enter this command: git fetch upstream

  • When you fetch changes from upstream, all new commits are downloaded and stored as a remote branch.

    • This new data is not integrated into your local files yet.
    • This allows you to check out and review the branch before merging it with your files. 

Step 8: Merge Changes

  • Use the Git > Merge command to merge changes from the upstream main branch into your local main branch.

    • Or you can enter this command:
      git merge upstream/main
  • This syncs your local main branch with the upstream repository without losing your local changes.
  • Your project now contains your work along with the work of others on your team.

Step 9: Push Changes

  • Push changes to the origin repository so everything is up-to-date.
    • Or you can enter this command:
      git push origin main

Repeat Steps 1-9

  • Step 1: Create an issue
  • Step 2: Create a new branch "yourname-contribution2".
  • Step 3: Add new packages and files.
    • This time add your controller classes and unit tests
  • Step 4: Commit (with a meaningful message) and push the new branch.
  • Step 5: Make a pull request. Wait for the maintainer to approve and merge the pull request.
  • Steps 6-8: Go back to the main branch. Fetch and merge (or pull) all changes from other contributors.
  • Step 9: Push the main branch to origin.

Original

Repo

Your

Copy

Local Computer

Fork it

Clone to

set Origin

Set Upstream

Fetch

Changes

 

Push Changes

Summary

  • Git is a distributed version control system that allows developers to save and retrieve different versions of their work.
  • A maintainer is a person that maintains an open source project.
  • A contributor is a person that contributes to another developer's open source project.
  • Forking is the process of creating a copy of a repository from one user's account to another. This allows contributors to make changes without affecting the maintainer's project.
  • Cloning means making a local copy of a project to your local machine.
  • Origin is the name of the remote alias from your local machine to your forked copy of the project.
  • Upstream is the name of the remote alias from your local machine to the original project.

Summary

  • A branch is a parallel version of a repository. It is contained within the repository, but does not affect the main branch allowing the developer to work freely without disrupting the "live" version.
  • After making changes on a branch and you want those changes to be incorporated in the "live" version, you can create a pull request. The owners/maintainers will review your changes and decide whether to merge them.
  • Developers can merge changes from other developers to the main branch.
  • Git has built-in mechanisms to help resolve potential conflicts. A merge conflict occurs when two developers make changes to the same file.

Summary

  • Here’s a picture that describes how the Git workflow works.
  • Note: Prior to 2020, master was the default branch name.

Java 2 - Week 9

By Marc Hauschildt

Java 2 - Week 9

  • 376