Introduction to Version Control with Git

Software Engineering and Course Logistics

Software engineering is distinct from solitary programming in that it is fundamentally defined by teamwork. This collaborative nature necessitates systems that allow multiple developers to work on a common code base, isolate experimental changes, and revert to previous states if mistakes occur. Within the university context, the group project dominating the second half of the course relies heavily on these principles. Currently, in week five, assignment two has been released for download. Notably, the team registrations for assignment two remain the same as those for assignment one, though students must ensure their groups are registered within the Teams app.

While there are no weekly labs during week five—only help desks on Monday—the following week's lab will involve Swing. While deep competence in Swing is not strictly required to complete the version control tasks of the labs, students are encouraged to engage with resources including videos by Jens and Marco, the official Oracle documentation, and GUI examples. These materials are accessible via the readings section of the course portal.

The Philosophy and Utility of Version Control

Version control acts as a sophisticated time machine for software development. It provides the ability to move back through the history of a project if an implementation takes a wrong turn. This functionality is the basis for delta debugging, a process where various combinations of recent code changes are tested to identify which specific modification triggered a bug. Git facilitates this through specialized commands. Beyond bug fixing, version control provides a complete log of project evolution, documenting who made specific changes, what those changes were, and exactly when they occurred through time-stamped commits. In a group project, consistent committing by every member—not just a lead programmer—is essential to maintain a transparent history of contributions.

Version Control Architectures and Approaches

There are two primary paradigms in version control: centralized and distributed. Centralized systems utilize a client-server architecture or a hub model. Distributed systems, such as Git, employ a flexible topology where every developer possesses their own local repository with a full history. Another distinction exists between locking and merging systems. Locking systems prevent multiple users from editing the same file simultaneously by requiring a user to lock the file before making changes. Merging systems, including Git, allow multiple developers to work on the same lines of code concurrently. When overlapping changes occur, the system identifies a conflict which developers must then resolve through a merging process.

Git was developed by Linus Torvalds, the creator of Linux. While many developers use Git in a client-server configuration using a central hub—typically a server such as GitHub, Bitbucket, or the internal GitLab system—every participant still maintains a full local repository. These individual repositories can be synchronized with the central remote repository through pushes and pulls.

The Three-Tier Structure of Git

Git operates through three main logical areas: the working folder, the staging area (also known as the index), and the local repository. The working folder contains the current state of the project files on the disk. When a repository is initialized using the command git init, a hidden folder named .git is created. This folder contains the entire history and metadata of the repository; essentially, any "memory" the version control system has is stored here.

Files in the working folder may be untracked. To include a file in a version snapshot, it must first be added to the staging area. The staging area acts as a buffer where developers can accumulate a coherent set of changes before finalizing them. A commit then transfers the contents of the staging area into the local repository. This separation is analogous to the Observer pattern, where one might set a "changed" flag before calling "notify."

Managing the Development Workflow

Effective Git usage involves a routine of checking status, staging, and committing. The command git status reveals which files are tracked, untracked, or modified. To track a file or stage a modification, the git add command is used. For example, git add hello.java moves that specific file to the index. It is recommended to treat adding and committing as a single atomic task to avoid confusion. If a file is staged on one day, then modified again in the working folder without being re-added, a subsequent commit will record the version from the previous day rather than the current state.

Commits are finalized using git commit -m "message", where the message provides a meaningful summary of the changes. These messages serve as a business card to other developers and should be kept high-quality. Using patterns like closes #21 in a commit message can automatically link the commit to an issue tracker and close the corresponding task or bug report. To view the project history, git log displays a list of commits with their unique identifiers, authors, and timestamps. For examining specific changes between the working directory and the last commit, git diff is utilized.

Optimizing Repository Content with Exclusion Rules

Not every file in a project folder belongs in version control. Derived files, such as .class files, should be excluded because they can be recreated from source code and would otherwise waste storage space. Similarly, IDE-specific metadata (e.g., .project files or classpath settings) and build target folders from tools like Maven or Ant should typically remain local.

To manage this, developers use a .git_ignore file. This file lists patterns of filenames and extensions that Git should disregard. For example, *.class would ignore all Java class files. This prevents Git from reporting these files as untracked in the status output. For broader management, a git_ignore_global file can apply rules across all projects on a machine. Tools like gitignore.io can generate complex, pre-configured ignore files for specific environments like Java, Maven, or Eclipse.

The Logical Structure of the Git History

Conceptually, a Git repository is a Directed Acyclic Graph (DAG) of commits. While it often resembles a simple linked list where each commit points to a parent, it becomes a graph when developers create branches for variants or merge two independent lines of development together. In Git's internal journaling, history grows upward, meaning the most recent commits are at the top of the graph.

Each commit is identified by a unique SHA-1 hash, which serves as a checksum for the contents of that version. To save space, Git does not store a full copy of every file for every commit. Instead, it calculates increments or differences between versions. If a file is mm bytes, and a modification is relatively small, the repository stores the original plus the size of the diff, rather than 2m2m bytes. This storage efficiency allows developers to make frequent snapshots without straining storage resources.

Collaborative Development and Remote Integration

In a team setting, a central repository—referred to as the "remote"—acts as the shared hub. Developers work locally, insulating their experiments from the rest of the team. Once a segment of work is complete, functional, and compiled, it is "pushed" to the remote. To receive the work of others, developers "pull" updates from the remote into their local repositories.

The command git remote -v verifies which remote servers a local repository is connected to. For the group project, a GitLab repository will be created once group memberships are finalized. While various graphical clients like Sourcetree, GitHub Desktop, GitKraken, or IDE-integrated tools in IntelliJ, VS Code, or BlueJ exist, understanding the command-line interface is critical for intervening when standard workflows fail.

User Identification and Authentication Protocols

To ensure proper attribution in the project log, Git must be configured with the user's name and email address. This is done via git config --global user.name and git config --global user.email, or by using git config --global --edit to modify the configuration file directly. It is vital that students use their VUW (Victoria University of Wellington) credentials and email addresses. This allows the GitLab server to recognize the user and enables instructors to accurately track contributions during project assessment. For secure communication with the GitLab servers, students should set up SSH credentials as outlined in the technical documentation provided on the course site.