Wednesday 13 March 2024

The Git & Github Bootcamp Part 6 - Master on essentials and the tricky bits: rebasing, squashing, stashing, reflogs, blobs, trees, & more!


Cleaning Up History with Interactive Rebase

1. Introducing Interactive Rebase

Interactive rebase (git rebase -i) allows you to modify commits in a more controlled manner. It opens a text editor showing a list of commits from the current branch relative to the specified base commit, along with options for how to alter each commit.

2. Rewording Commits With Interactive Rebase

To reword a commit means to change the commit message without altering the snapshot.

  • Start an interactive rebase for the last n commits: git rebase -i HEAD~n.
  • In the editor that opens, change pick to reword (or r) next to the commit you want to reword.
  • Save and close the editor. Git will open a new editor window for each commit you chose to reword.
  • Update the commit message, save, and close the editor to continue.

3. Fixing Up & Squashing Commits With Interactive Rebase

Squashing combines multiple commits into a single commit, while fixing up is similar but discards the commit message of the squashed commit.

  • During an interactive rebase (git rebase -i HEAD~n), change pick to squash (or s) or fixup (or f) for the commits you want to combine.
  • For squashing, after saving and closing the initial editor, you’ll be prompted to create a new commit message for the squashed commits.
  • For fixing up, Git automatically discards the commit messages of fixed-up commits and keeps the first commit’s message.

4. Dropping Commits With Interactive Rebase

Dropping a commit removes it from the history.

  • Start the interactive rebase: git rebase -i HEAD~n.
  • In the editor, delete the line corresponding to the commit you want to drop, or change pick to drop (or d).
  • Save and close the editor to complete the rebase.

Interactive Rebase Exercise with “SampleProject”

  1. Prepare: Create a new branch off main for this exercise: git switch -c cleanup-exercise.
  2. Make some commits: Create dummy files or make changes in your project, committing each change.
  3. Reword a commit: Choose a commit to reword and start an interactive rebase: git rebase -i HEAD~n. Replace n with the number of commits you want to review.
  4. Squash commits: Pick a few commits to squash into a single commit. Use the interactive rebase editor to mark these commits with squash.
  5. Drop a commit: If there’s a commit you no longer need, use the interactive rebase to drop it.

Through this exercise, “SampleProject” not only demonstrates the utility of cleaning up your commit history for easier navigation and understanding but also highlights interactive rebase as a powerful tool for ensuring your project’s history is as clean and concise as possible.

Let’s incorporate the concept of Git tags into our “SampleProject” to demonstrate marking significant milestones, such as version releases. Tags in Git serve as bookmarks to specific points in the repository’s history, allowing easy access to certain versions.

Git Tags: Marking Important Moments In History

1. The Idea Behind Git Tags

Tags are references that point to specific points in a Git repository’s history. They are most commonly used to mark release points (v1.0, v2.0, etc.).

2. A Side Note On Semantic Versioning

Semantic Versioning (SemVer) is a versioning scheme for software that specifies how version numbers are assigned and incremented. For example, version numbers are displayed as MAJOR.MINOR.PATCH (e.g., 1.4.2), where:

  • MAJOR version increases make incompatible API changes,
  • MINOR version increases add functionality in a backward-compatible manner,
  • PATCH version increases make backward-compatible bug fixes.

3. Viewing & Searching Tags

To list all tags in a repository:

git tag

To search for tags with a particular pattern:

git tag -l "v1.*"

4. Comparing Tags With Git Diff

To see the differences between two versions of a project:

git diff tag1 tag2

Replace tag1 and tag2 with the tags you wish to compare.

5. Creating Lightweight Tags

A lightweight tag is like a branch that doesn’t change – it’s just a pointer to a specific commit.

git tag v1.0

This command creates a lightweight tag named v1.0 at the current commit.

6. Creating Annotated Tags

Annotated tags, recommended for most use cases, are stored as full objects in the Git database. They include the tagger name, email, date, and have a tagging message.

git tag -a v1.1 -m "Release version 1.1"

This creates an annotated tag v1.1 with the message “Release version 1.1.”

7. Tagging Previous Commits

To tag a commit other than HEAD, specify the commit hash at the end of your tag creation command:

git tag -a v1.0.1 <commit-hash> -m "Patch release fixing a bug"

8. Replacing Tags With Force

If you need to move a tag to a different commit, use the -f option:

git tag -a v1.0 -m "Updated release" -f <commit-hash>

Use this with caution as it changes the tag’s target commit.

9. Deleting Tags

To delete a local tag:

git tag -d v1.0

10. IMPORTANT: Pushing Tags

Tags do not automatically get pushed to remote when you git push. To push a specific tag:

git push origin v1.0

To push all tags:

git push origin --tags

Exercise with “SampleProject”

  • Mark the current state as a new version with an annotated tag: git tag -a v2.0 -m "Major release with new features."
  • Push the tag to GitHub to share the release: git push origin v2.0
  • List all tags to view your project’s milestones: git tag

Through tagging, “SampleProject” not only signifies important milestones like version releases but also utilizes tags to document and navigate the project’s evolution efficiently.

For our ongoing “SampleProject,” let’s delve into Git’s internals to understand how it manages data and configurations. This exploration will cover the foundational elements that make Git a powerful version control system, focusing on its local configuration, the structure of its directory, and the principles of its object storage.

Git Behind the Scenes - Hashing & Objects

1. Working With The Local Config File

Git configurations can be found in the .git/config file within any Git repository. This file contains settings specific to the repository, such as remote repository addresses and branch configurations.

2. Inside Git: The Refs Directory

The .git/refs directory holds references to commits, primarily branches (refs/heads) and tags (refs/tags). These references are pointers to commit objects, enabling Git to quickly navigate the project’s history.

3. Inside Git: The HEAD File

.git/HEAD is a reference to the current branch or commit that’s checked out. If you’re on a branch, it will point to the tip of that branch in the refs directory. When detached, it directly points to a commit hash.

4. Inside Git: The Objects Directory

.git/objects stores all the data for your repository. This includes commits, tree objects (which represent directory contents), and blob objects (which represent file contents). Each object is named and accessed by its SHA-1 hash.

5. A Crash Course On Hashing Functions

Hashing functions, like SHA-1 used in Git, take input data and produce a fixed-size string of characters, the hash. Regardless of the input’s size, the output (hash) has a fixed length. Git uses this for efficiency and integrity checking.

6. Git As A Key-Value Datastore

Git’s objects directory functions as a key-value datastore, where the key is the SHA-1 hash of the object’s contents, and the value is the object itself. This design supports data integrity, deduplication, and efficient retrieval.

7. Hashing With Git Hash-Object

To create a hash for a specific file and store it as a blob object in the .git/objects directory, use:

git hash-object -w <filename>

The -w option tells Git to write the object into the objects directory.

8. Retrieving Data With Git Cat-File

You can retrieve the contents and type of any object in the .git/objects directory using:

git cat-file -p <hash>

The -p option prints the object’s contents. Replace <hash> with the object’s SHA-1 hash.

9. Deep Dive Into Git Objects: Blobs

Blob objects represent the contents of files in a Git repository, not the filenames or directory structure. Each file’s content is stored in a separate blob, identified by its hash.

10. Deep Dive Into Git Objects: Trees

Tree objects represent the directory structure of a Git repository at a certain point in time. A tree object contains pointers to blob objects (files) and other tree objects (directories), along with their names and permissions.

11. Deep Dive Into Git Objects: Commits

A commit object points to a tree object representing the state of the repository at the time of the commit. It also contains metadata, including the author, committer, date, and commit message. Each commit object points to its parent commit(s), forming the repository’s history.

Experiment with “SampleProject”

To solidify your understanding of Git’s internals, try experimenting with the commands mentioned above. For instance, use git hash-object to hash a file in your project, then retrieve it with git cat-file. Explore the .git directory to familiarize yourself with the structure and contents of Git objects. This hands-on exploration will deepen your grasp of how Git manages and stores your project data.

Labels:

0 Comments:

Post a Comment

Note: only a member of this blog may post a comment.

<< Home