352 Research ‐ Git Basics - bounswe/bounswe2024group1 GitHub Wiki

Git: The Version Control Software

Overview

Concepts

Objects in Git

Git is fundamentally a content-addressable filesystem. This means it uses the content itself to generate an address that can uniquely identify it. There are four main types of objects:

  • Blobs

    Represent the content of a file in the repository.

  • Trees

    Represent the structure of a directory. It references blobs and other trees (subdirectories).

  • Commits

    Capture the state of the repository at a point in time. A commit references a tree that represents the top-level directory of the repository and links to parent commit(s).

  • Tags

    Marks specific commits with human-readable names like version numbers. Optionally annotated.

Commits

A commit in Git is a snapshot of your entire repository at one point in time. Each commit contains a reference to the tree object that captures the state of the repository, metadata about the commit (author, message, etc.), and references to parent commit(s) allowing the reconstruction of the commit history.

Hashes

Git uses SHA-1 hashes to uniquely identify objects within the repository. These hashes are generated based on the content of the object, ensuring that any change to the content results in a different hash. This mechanism underpins Git's integrity and the ability to quickly compare objects.

Refs

Refs are references to commits. The most common types of refs are branches and tags. Refs provide human-readable pointers to SHA-1 hashes, allowing easier navigation and manipulation of the commit history.

Branches

Branches in Git are simply lightweight movable pointers to commits. Branches are a type of ref, like tags. The default branch in Git is master (or main in newer repositories including GitHub). Branches allow developers to work in parallel on different features or fixes without interfering with each other's work.

Trees

A tree object in Git represents a directory. It contains a list of filenames and their corresponding blobs (file content) or other trees (subdirectories), along with each file's mode (executable, symbolic link, etc.), type, and SHA-1 hash.

Remotes

A remote in Git is a common repository that all team members use to exchange their changes. In most cases, this is a repository hosted on a server like GitHub, GitLab, or Bitbucket. Each clone of a repository is a repository with its own history and branches, and remotes are used to synchronize changes between repositories, allowing multiple contributors to synchronize their code.

Merging

Merging is the process of integrating changes from one branch into another. Git supports several types of merges, including fast-forward merges and three-way merges. Conflicts may happen during a merge if the changes in the branches cannot be merged automatically; Git requires these to be resolved manually.

Staging Area (Index)

A layer that holds the changes which will be included in the next commit. This allows for selective commits and more precise control over the repository's history.

Common Commands

git add

The git add command adds a change in the working directory to the staging area. It tells Git that you want to include updates to a particular file in the next commit. However, git add doesn't really affect the repository in any significant way—changes are not actually recorded until you run git commit An example usage is:

git add README.md

Below is some common usages.

git add <path># Stage a specific directory or file
git add .# Stage all files (that are not listed in the .gitignore) in the entire repository
git add -p# Interactively stage hunks of changes

git commit

The git commit command is used for changes that are staged with 'git add' to be turned into a commit. Also if you want to skip the staging part, you can add '-a' parameter to your git commit command to directly commit the changes. It is essential to use the command with a commit message using the '-m' parameter. Example usage = git commit -m "Message"

git branch

  • In Git, a branch is essentially a lightweight movable pointer to a commit. When you commit changes in Git, the commit moves forward automatically, and the branch moves with it. This mechanism allows you to work on different features, bug fixes, or experiments independently of each other.
  • Git branches are used to isolate work, experiment with new features, and collaborate on different aspects of a project simultaneously. Here's a brief description and some commonly used commands

This command lists all the branches in your repository. It's useful to see what branches currently exist and which one you are currently on.
git branch

This command creates a new branch with the specified name. It doesn't switch to the new branch; it simply creates it.

git branch [branch_name]

This command is used to switch between branches or to create a new branch and switch to it in a single command.

git checkout

Or to create a new branch and switch to it:

git checkout -b [new_branch]

This command deletes the specified branch.

git branch -d [branch_name] 

This command forcefully deletes the specified branch, even if it has unmerged changes.

git branch -D [branch_name] 

This command renames the specified branch.

git branch -m [old_branch_name] [new_branch_name]

References 🔗

git merge

The git merge command is used to combine the changes from multiple branches into a single branch. In other words, the concept of merging is basically to integrate multiple sequences of commits, stored in multiple branches in a unified history.

Team members will work on different branches to develop the code during the project, after completion we can merge them into a single version of the code.

Example:

git checkout main  # switch to the target branch (main)
git merge another_branch  # merge changes from another_branch into main

How does git merge works ?

When we try to merge two branches, git takes two commit pointers and starts searching for a common base commit in those two specified bit branches. When git finds the common base commit it simply creates a “merge commit” automatically and merges each queued merge commit sequence.

Pre-merge checks :

  • Execute git status to ensure that HEAD is pointing to the correct merge-receiving branch. If needed, execute git checkout  to switch to the receiving branch.
  • Make sure the receiving branch and the merging branch are up-to-date with the latest remote changes. Execute git fetch to pull the latest remote commits. Once the fetch is completed ensure the main branch has the latest updates by executing git pull.

There are two main types of merging:

    1. Fast-Forward Merging:

Fast forward merge happens when the tip of the current branch is a direct ancestor of the target branch. Here instead of actually merging the two branches git simply moves the current branch tip up to the target branch tip.

This behavior can be suppressed with the --no-ff option.

    1. Three-Way Merging:

When the base branch has changed since the branch was first created, this kind of merging takes place. Git in this situation generates a fresh merging commit that incorporates the modifications from both branches. Git compares the modifications made to both branches with those made to the base branch using a three-way merge process. Following that, it integrates both sets of changes into a single new commit.

Merge Conflicts:

When Git encounters a conflict during a merge, It will edit the content of the affected files with visual indicators that mark both sides of the conflicted content. These visual markers are: <<<<<<<, =======, and >>>>>>>. It's helpful to search a project for these indicators during a merge to find where conflicts need to be resolved.

Generally the content before the ======= marker is the receiving branch and the part after is the merging branch. Note that merge conflicts will only occur in the event of a 3-way merge. It’s not possible to have conflicting changes in a fast-forward merge.

How to resolve merge conflicts ?

After seeing a conflict, you can do two things:

  • Decide not to merge. The only clean-ups you need are to reset the index file to the HEAD commit to reverse 2. and to clean up working tree changes made by 2. and 3.; git merge --abort can be used for this.
  • Resolve the conflicts. Git will mark the conflicts in the working tree. Edit the files into shape and git add them to the index. Use git commit or git merge --continue to seal the deal. The latter command checks whether there is a (interrupted) merge in progress before calling git commit.

Some Common Options:

  • -commit
  • -no-commit

Perform the merge and commit the result. This option can be used to override --no-commit. With --no-commit perform the merge and stop just before creating a merge commit, to give the user a chance to inspect and further tweak the merge result before committing. Note that fast-forward updates do not create a merge commit and therefore there is no way to stop those merges with --no-commit. Thus, if you want to ensure your branch is not changed or updated by the merge command, use --no-ff with --no-commit.

  • -ff
  • -no-ff
  • -ff-only

Specifies how a merge is handled when the merged-in history is already a descendant of the current history. --ff is the default unless merging an annotated (and possibly signed) tag that is not stored in its natural place in the refs/tags/ hierarchy, in which case --no-ff is assumed. With --ff, when possible resolve the merge as a fast-forward (only update the branch pointer to match the merged branch; do not create a merge commit). When not possible (when the merged-in history is not a descendant of the current history), create a merge commit. With --no-ff, create a merge commit in all cases, even when the merge could instead be resolved as a fast-forward. With --ff-only, resolve the merge as a fast-forward when possible. When not possible, refuse to merge and exit with a non-zero status.

Check Git documentation for more detailed explanation:

https://git-scm.com/docs/git-merge

References:

git push

The git push command is used to upload local repository content to a remote repository. Pushing is how you transfer commits from your local repository to a remote repo. It's the counterpart to git fetch, but whereas fetching imports commits to local branches, pushing exports commits to remote branches. Pushing has the potential to overwrite changes, caution should be taken when pushing.

References:

Interesting Git Features and Customization

Further Resources

Here are some resources that talk about git and its features.

⚠️ **GitHub.com Fallback** ⚠️