{"id":31842668,"url":"https://github.com/fareedkhan-dev/git-workflow-guide","last_synced_at":"2025-10-12T06:49:00.544Z","repository":{"id":308988110,"uuid":"1034446534","full_name":"FareedKhan-dev/git-workflow-guide","owner":"FareedKhan-dev","description":"26 different problems dev teams faces with Git","archived":false,"fork":false,"pushed_at":"2025-08-09T03:15:13.000Z","size":32,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-09T05:29:25.755Z","etag":null,"topics":["developer","git","github","workflow"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FareedKhan-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-08T12:03:02.000Z","updated_at":"2025-08-09T03:15:17.000Z","dependencies_parsed_at":"2025-08-09T11:00:49.284Z","dependency_job_id":null,"html_url":"https://github.com/FareedKhan-dev/git-workflow-guide","commit_stats":null,"previous_names":["fareedkhan-dev/git-workflow-guide"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/FareedKhan-dev/git-workflow-guide","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FareedKhan-dev%2Fgit-workflow-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FareedKhan-dev%2Fgit-workflow-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FareedKhan-dev%2Fgit-workflow-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FareedKhan-dev%2Fgit-workflow-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FareedKhan-dev","download_url":"https://codeload.github.com/FareedKhan-dev/git-workflow-guide/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FareedKhan-dev%2Fgit-workflow-guide/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279010531,"owners_count":26084759,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["developer","git","github","workflow"],"created_at":"2025-10-12T06:48:59.172Z","updated_at":"2025-10-12T06:49:00.537Z","avatar_url":"https://github.com/FareedKhan-dev.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- omit in toc --\u003e\n# 26 Git Workflow Challenges\n\n![Art illustration by Anni Roenkae](https://miro.medium.com/v2/resize:fit:2236/1*cPZg05aeDCZJNYhNntBXNw.png)\n*Art illustration by [Anni Roenkae](https://www.pexels.com/photo/abstract-wallpaper-2860804/) (Pexels)*\n\nAs a programmer, you know that version control gets complex when a codebase becomes large and is handled by a team of developers.\n\nYour team might be working with [GitHub](https://github.com/), [GitLab](https://about.gitlab.com/), [BitBucket](https://bitbucket.org/product/), or any other Version Control System but …\n\n\u003e There are certain ethics of Git that, if you don’t follow, you might end up making the codebase a total mess.\n\nIn this post, I will share how my development team debugs and resolves issues **we have encountered over the past four years of working with Git**.\n\n\u003c!-- omit in toc --\u003e\n## Table of Contents\n- [1. Working on a Remote Server/VM with My Team, What Should We Do First?](#1-working-on-a-remote-servervm-with-my-team-what-should-we-do-first)\n- [2. Our Repo is Huge and `git clone` Takes Forever. What Can I Do?](#2-our-repo-is-huge-and-git-clone-takes-forever-what-can-i-do)\n- [3. How Can We Automatically Run Linters or Tests Before Anyone Commits?](#3-how-can-we-automatically-run-linters-or-tests-before-anyone-commits)\n- [4. I Accidentally Committed a Password/API Key, How Do I Erase It From History?](#4-i-accidentally-committed-a-passwordapi-key-how-do-i-erase-it-from-history)\n- [5. A Bug Was Introduced Weeks Ago, How Can I Find the Exact Commit That Broke Everything?](#5-a-bug-was-introduced-weeks-ago-how-can-i-find-the-exact-commit-that-broke-everything)\n- [6. Our Project Depends on Another Repo, Should We Use Submodules or Subtrees?](#6-our-project-depends-on-another-repo-should-we-use-submodules-or-subtrees)\n- [7. How Do We Properly Tag Releases and Manage Hotfixes on Old Versions?](#7-how-do-we-properly-tag-releases-and-manage-hotfixes-on-old-versions)\n- [8. I Messed Up and Lost My Commits. Is There a Way to Get Them Back?](#8-i-messed-up-and-lost-my-commits-is-there-a-way-to-get-them-back)\n- [9. Who Wrote This Line of Code and Why?](#9-who-wrote-this-line-of-code-and-why)\n- [10. How Do I Keep My Fork in Sync with the Original ‘Upstream’ Repo?](#10-how-do-i-keep-my-fork-in-sync-with-the-original-upstream-repo)\n- [11. How Do I Prove My Commits Really Came From Me? (Signing Commits)](#11-how-do-i-prove-my-commits-really-came-from-me-signing-commits)\n- [12. What is a “Detached HEAD” State and How Do I Fix It Without Losing My Work?](#12-what-is-a-detached-head-state-and-how-do-i-fix-it-without-losing-my-work)\n- [13. Our Repo is Full of Large Assets, How Do We Handle Them Without Bloating the History?](#13-our-repo-is-full-of-large-assets-how-do-we-handle-them-without-bloating-the-history)\n- [14. This Monorepo is Too Big. How Do I Split a Folder into a New Repository?](#14-this-monorepo-is-too-big-how-do-i-split-a-folder-into-a-new-repository)\n- [15. I Committed the Wrong Code, How Do I Undo My Error?](#15-i-committed-the-wrong-code-how-do-i-undo-my-error)\n- [16. When My Work Collided with a Teammate Changes, Now I Can’t Pull?](#16-when-my-work-collided-with-a-teammate-changes-now-i-cant-pull)\n- [17. Why Did Applying My Stash Cause Conflicts?](#17-why-did-applying-my-stash-cause-conflicts)\n- [18. I Just Committed but Forgot to Add a Few Files?](#18-i-just-committed-but-forgot-to-add-a-few-files)\n- [19. My Branch Name Doesn’t Match the Work Anymore?](#19-my-branch-name-doesnt-match-the-work-anymore)\n- [20. You Already Pushed Your Commit, But Now You Need to Update It?](#20-you-already-pushed-your-commit-but-now-you-need-to-update-it)\n- [21. My Push Rejected, I Then Pulled… and Now Conflicts Everywhere!](#21-my-push-rejected-i-then-pulled-and-now-conflicts-everywhere)\n- [22. Push Rejected Again? This Time Let’s Rebase Instead of Merging](#22-push-rejected-again-this-time-lets-rebase-instead-of-merging)\n- [23. My PR Shows Conflicts, How Do I Fix It Before the Team Yells at Me?](#23-my-pr-shows-conflicts-how-do-i-fix-it-before-the-team-yells-at-me)\n- [24. I Have Too Many Commits!](#24-i-have-too-many-commits)\n- [25. I Committed to One Branch but Need the Same Changes in Another](#25-i-committed-to-one-branch-but-need-the-same-changes-in-another)\n- [26. I Merged My Changes… Then Realized They Shouldn’t Be There](#26-i-merged-my-changes-then-realized-they-shouldnt-be-there)\n\n## 1. Working on a Remote Server/VM with My Team, What Should We Do First?\nAs an AI Engineer, our local laptops or PCs don’t always have the best GPUs to handle AI models. In those cases, it’s enough to just run a simple `git clone ...` command to grab the organization repo.\n\n**But when working on a VM, we developers want to be a bit more secure.** Usually, we avoid logging in with our Git credentials directly on a shared VM, because that environment can be accessible or visible to other dev teams.\n\n\u003e To stay safe, we use the SSH approach to authenticate with Git and avoid exposing our credentials.\n\nThe process is pretty straightforward, in your VM terminal, you just need to generate an SSH key using the command:\n\n```bash\n# Generating Public keys to link your VM\nssh-keygen -t ed25519 -C \"your_email@example.com\"\n```\n\nJust hit enter for all the prompts, unless you want to specify a custom file name or passphrase. After that, you’ll find your public key in `~/.ssh/id_ed25519.pub`.\n\nNext, you copy that public key using:\n\n```bash\n# Printing the generated key\ncat ~/.ssh/id_ed25519.pub\n```\n\nNow head over to your Git hosting service (like GitHub, GitLab, etc.), go to your profile → SSH keys section, and paste it there as a new key.\n\n![GitHub SSH Web Screenshot](https://miro.medium.com/v2/resize:fit:875/1*FLbZYe51RWfGjzIfzfvryQ.png)\n\nOnce that’s done, back in your VM, you can clone your org repo using SSH instead of HTTPS:\n\n```bash\n# Cloning your org repo\ngit clone git@github.com:your-org/your-repo.git\n```\n\n\u003e No credentials prompt, no exposure, and much safer in a shared VM environment.\n\n## 2. Our Repo is Huge and `git clone` Takes Forever. What Can I Do?\nYou’re working on a massive monorepo with years of history. Cloning it takes 20 minutes and eats up 5 GB of disk space.\n\n\u003e This is especially painful for CI/CD pipelines that need to clone the repo for every build.\n\nYou don’t always need the entire project history just to run tests or build a feature. For these cases, you can perform **a shallow clone**.\n\nA **shallow clone** downloads only the most recent `n` commits, dramatically reducing clone time and disk usage.\n\n```bash\n# Perform a shallow clone, getting only the very latest commit.\ngit clone --depth 1 https://github.com/massive-org/massive-repo.git\n\n### Terminal Output ###\nCloning into 'massive-repo'...\nremote: Enumerating objects: 1500, done.\nremote: Counting objects: 100% (1500/1500), done.\nremote: Compressing objects: 100% (1200/1200), done.\nremote: Total 1500 (delta 300), reused 1000 (delta 200), pack-reused 0\nReceiving objects: 100% (1500/1500), 50.23 MiB | 10.50 MiB/s, done.\nResolving deltas: 100% (300/300), done.\n\n# The clone finishes in seconds instead of minutes.\n\n# The trade-off: your local history is gone.\n# Commands like `git log` will only show one commit.\n```\n\nIf you later decide you need more history, you can “deepen” your clone:\n\n```sql\n# Fetch the next 50 commits\ngit fetch --depth 50\n\n# Or fetch the entire history and convert it to a normal clone\ngit fetch --unshallow\n```\n\n\u003e Shallow cloning is an optimization technique for working efficiently with large-scale repositories, especially in automated environments.\n\n## 3. How Can We Automatically Run Linters or Tests Before Anyone Commits?\nA teammate pushes code with syntax errors. Another pushes a change that breaks a unit test. These mistakes are simple, but they disrupt the workflow and break the build for everyone else.\n\n\u003e You can prevent this entirely by using Git Hooks. These are scripts that automatically run at certain points in the Git lifecycle.\n\nThe most useful one is the `pre-commit` hook, which runs *before* a commit is created. If the script fails, the commit is aborted.\n\nWhile you can write these scripts by hand in the `.git/hooks` directory, a much easier way is to use a framework like [pre-commit](https://pre-commit.com/).\n\nHere’s the concept:\n\n```yaml\n# You create a .pre-commit-config.yaml file in your repo root\n\nrepos:\n-   repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v4.3.0\n    hooks:\n    -   id: check-yaml           # Checks YAML file syntax\n    -   id: end-of-file-fixer    # Ensures files end in a newline\n    -   id: trailing-whitespace  # Trims trailing whitespace\n\n-   repo: https://github.com/psf/black\n    rev: 22.6.0\n    hooks:\n    -   id: black                # Runs the Black Python code formatter\n```\n\nWhen a developer on your team runs `git commit`:\n\n```bash\n$ git commit -m \"Add new feature\"\n\n### Terminal Output ###\nCheck Yaml...........................................Passed\nFix End of Files.....................................Passed\nTrim Trailing Whitespace.............................Passed\nblack................................................Failed\n- hook id: black\n- files were modified by this hook\n\n# The commit is aborted because the Black formatter found issues and fixed them.\n# The developer just needs to `git add` the fixed files and commit again.\n```\n\n\u003e Git hooks act as an automated quality gatekeeper. They enforce code standards and prevent simple mistakes from ever reaching your shared repository.\n\n## 4. I Accidentally Committed a Password/API Key, How Do I Erase It From History?\n\u003e This ones normally comes from interns.\n\nYou push your code, only to realize you accidentally included a `.env` file, a private key, or a hardcoded password.\n\nJust deleting the file and making a new commit is not enough. The secret is still buried in your Git history, visible to anyone who clones the repo. You need to wipe it from existence, completely. This calls for a tool that rewrites history.\n\nThe modern way to do this is using `git filter-repo`.\n\n```bash\n# First, remove the sensitive file from your working directory.\n# Make sure it's also in your .gitignore so you don't do this again!\nrm path/to/your/secret.file\n\n# Run the filter-repo command to remove the file from ALL commits.\n# --invert-paths tells it to remove the specified file.\ngit filter-repo --path path/to/your/secret.file --invert-paths\n\n### Terminal Output ###\nParsed 123 commits\n...\nRepacked objects: 100% (456/456), done.\nNew history written in .git/filter-repo/analysis/....\n\n# This operation rewrites history, so you MUST force push.\n# Use --force-with-lease for safety, but --force is needed here.\ngit push origin --force --all\n```\n\n\u003e **After doing this, you must treat the leaked secret as compromised.**\n\nInvalidate the API key, change the password, and rotate your credentials immediately. This Git command cleans your history, but it can’t undo the fact that the secret was exposed, even if briefly.\n\n## 5. A Bug Was Introduced Weeks Ago, How Can I Find the Exact Commit That Broke Everything?\n\u003e Your app was working fine last Monday, but the following Friday, while you were wrapping up your codebase, a subtle bug surfaced.\n\nYou have hundreds of commits between then and now. Manually checking out each one to find the culprit would take all day.\n\nThis is where we normally use `git bisect`, Git’s built-in bug detective. It performs an automatic binary search through your commit history, asking you at each step if the bug is present.\n\n```bash\n# 1. Start the bisect process\ngit bisect start\n\n# 2. Tell Git the current commit is broken\ngit bisect bad\n\n# 3. Tell Git the last known commit where everything worked\n# You can use a tag (v1.2.0) or a commit hash\ngit bisect good v1.2.0\n\n### Terminal Output ###\nBisecting: 67 revisions left to test after this (roughly 6 steps)\n[f3a9b8c] feat: Add user profile caching\n\n# Now, test your app. Is the bug here?\n# Let's say it is. Tell Git:\ngit bisect bad\n\n### Terminal Output ###\nBisecting: 33 revisions left to test after this (roughly 5 steps)\n[e1d4c5b] refactor: Update auth middleware\n\n# Test again. This time, it works! Tell Git:\ngit bisect good\n\n# ... and so on. Git will narrow it down until...\n### Terminal Output ###\na9b7c6d is the first bad commit\ncommit a9b7c6d\nAuthor: Your Teammate \u003cemail@example.com\u003e\nDate:   Wed Jul 17 11:22:13 2024 -0500\n\n    feat: Optimize image loading\n\n# Once you've found the culprit, end the bisect session\ngit bisect reset\n```\n\n\u003e With `git bisect`, you can find a single bad commit in a sea of hundreds in just a few minutes, It saves us around like 2 to 3 hours of tedious guesswork.\n\n## 6. Our Project Depends on Another Repo, Should We Use Submodules or Subtrees?\nIn an organization’s repo, dependencies can conflict …\n\n\u003e like in Python, where an older module version may not work with a newer dependent module, causing pipeline crashes.\n\nYour project needs to include another repository maybe a shared internal library, a third-party module, or a separate microservice. How do you manage this dependency? You have two main options: `git submodule` and `git subtree`.\n\nA **submodule** is essentially a pointer to a specific commit in another repository. Your main repo doesn’t contain the submodule’s code, just a link to it.\n\n```bash\n# Add a submodule to your project\ngit submodule add https://github.com/your-org/shared-library.git libs/shared-library\n\n# When someone clones your repo, they need an extra step:\ngit clone https://github.com/your-org/main-project.git\ncd main-project\ngit submodule update --init --recursive\n```\n\n*   **Best for** keeping projects neatly separated. You can update a library without cluttering your main repo’s history.\n*   **Downside** is that it adds complexity because everyone must remember to run `git submodule update`.\n\nOn the other hand, **subtree** copies the entire history of the other repository directly into your main project’s history, placing its files in a subdirectory.\n\n```bash\n# Add a subtree to your project\n# The --squash flag keeps your history cleaner\ngit subtree add --prefix=libs/shared-library https://github.com/your-org/shared-library.git main --squash\n```\n\n*   **Best for** simplicity. Anyone who clones your repo gets all the code in one go with no extra steps.\n*   **Downside** is that it can make your main repository history larger and more complex, and pulling updates from the library becomes a more manual process.\n\n\u003e Use submodules when you need strict versioning and separation. Use subtrees when you prioritize ease-of-use for your team and want a self-contained repository.\n\n## 7. How Do We Properly Tag Releases and Manage Hotfixes on Old Versions?\nYour team just released `v2.0.0`. A week later, a critical security bug is found in the `v1.5.0` release, which is still used by major clients. The `main` branch has moved on, so you can't just commit a fix there.\n\nThis is where a disciplined branching and tagging strategy, like GitFlow, becomes essential. The key is to create a `hotfix` branch from the old release tag.\n\n```bash\n# 1. Check out the old version tag into a new hotfix branch\ngit checkout -b hotfix/v1.5.1 v1.5.0\n\n# 2. Make your code changes to fix the bug\n# (Edit files here...)\ngit add .\ngit commit -m \"Hotfix: Patch critical security vulnerability in legacy API\"\n\n# 3. Merge the hotfix back into main (so future versions get the fix)\ngit checkout main\ngit merge hotfix/v1.5.1\n\n# 4. Also merge it into your develop branch (if you use one)\ngit checkout develop\ngit merge hotfix/v1.5.1\n\n# 5. Tag the hotfix as a new release and push\ngit tag -a v1.5.1 -m \"Version 1.5.1\"\ngit push origin v1.5.1\n\n# 6. Don't forget to push your updated main and develop branches\ngit push origin main develop\n```\n\nThis way we make sure that you can patch old versions without disrupting current development, while also guaranteeing the fix is incorporated into all future releases.\n\n## 8. I Messed Up and Lost My Commits. Is There a Way to Get Them Back?\nYou ran `git reset --hard` to discard some changes, but you went back too far and wiped out the last three hours of work. Or maybe you deleted a branch without merging it first. Your commits are gone. There's a moment of pure panic.\n\n\u003e Before you start re-coding everything from memory, take a breath. Git has a secret safety net, the reflog.\n\nThe **reflog** is a private log of every move your `HEAD` pointer has made. Committing, resetting, switching branches, Git remembers it all, even if the commits are no longer part of any branch. It’s your personal undo history for Git itself.\n\n```bash\n# Display the history of all your recent actions\ngit reflog\n\n### Terminal Output ###\na1b2c3d HEAD@{0}: reset: moving to HEAD~3\nf4e5d6c HEAD@{1}: commit: Add user dashboard component\ng7h8i9j HEAD@{2}: commit: Fix login validation rules\nk1l2m3n HEAD@{3}: checkout: moving from main to feature-dashboard\n\n# In this log, my last three commits (f4e5d6c, g7h8i9j, k1l2m3n)\n# were wiped out by the reset. But they are still there in the reflog!\n\n# To restore your work, find the commit you want to return to (e.g., f4e5d6c).\n# You can create a new branch from it to be safe.\ngit checkout -b recovered-work f4e5d6c\n\n# Or, if you're confident, reset your current branch back to that state\ngit reset --hard f4e5d6c\n```\n\n\u003e The reflog only stores history for a limited time (90 days by default) and is local to your machine.\n\n## 9. Who Wrote This Line of Code and Why?\nYou are digging through the codebase and find a strange, uncommented line of code. You have no idea what it does, why it’s there, or if it’s safe to change. To understand it, you need context.\n\n\u003e You need to find out *who* wrote it and *what else* was in that same commit.\n\nThis is the job for `git blame`. Despite its aggressive name, it’s not for pointing fingers, it’s for understanding the history of a file, line by line.\n\n```bash\n# Run blame on a file to see the author and commit for every line\ngit blame app/services/payment-processor.js\n\n### Terminal Output ###\n^a9b8c7 (Jane orteg  2024-07-15 10:30:15 -0500  1) const processPayment = (amount) =\u003e {\nf4e5d6c (John Ssuza  2024-08-01 14:05:21 -0500  2)   if (amount \u003c 0) {\nf4e5d6c (John Ssuza  2024-08-01 14:05:21 -0500  3)     // Temp fix for negative values, per TICKET-123\nf4e5d6c (John Ssuza  2024-08-01 14:05:21 -0500  4)     return { success: false, error: 'Invalid amount' };\n^a9b8c7 (Jane oteg   2024-07-15 10:30:15 -0500  5)   }\nd1e2f3g (You         2024-08-20 09:12:45 -0500  6)   // ... more code\n...\n\n# The output shows the commit hash, author, date, and line number.\n# Line 3 is the one we're curious about. It belongs to commit f4e5d6c.\n# Now, use that hash to see the full commit message and changes.\ngit show f4e5d6c\n\n### Terminal Output ###\ncommit f4e5d6c1a2b3d4e5f6g7h8i9j0k1l2m3n4o5p6q\nAuthor: John Smith \u003cjohn.s@example.com\u003e\nDate:   Thu Aug 1 14:05:21 2024 -0500\n\n    Hotfix: Prevent crashes from negative payment amounts\n\n    A production incident (TICKET-123) showed that a downstream\n    service was sending negative values, causing a crash. This\n    adds a temporary guard until the other service is fixed.\n\ndiff --git a/app/services/payment-processor.js b/app/services/payment-processor.js\n...\n```\n\nWith `git blame` and `git show`, you can go from\n\n\u003e What is this weird code?\n\nto\n\n\u003e Ah, it's a temporary hotfix for TICKET-123, I shouldn't remove it yet\n\nin under a minute.\n\n## 10. How Do I Keep My Fork in Sync with the Original ‘Upstream’ Repo?\nThis is the standard workflow in open source and many large organizations. You fork the main company repository, clone your fork locally, and do your work there.\n\nBut while you’re working on your feature branch, other developers are merging their changes into the original repository.\n\n\u003e Your fork might becomes outdated, leading to merge conflicts when you finally open a Pull Request.\n\nTo prevent this, you need to configure a second remote, traditionally called `upstream`, that points to the original repository.\n\n```bash\n# 1. Check your current remotes (you should only see 'origin')\ngit remote -v\n\n### Terminal Output ###\norigin  git@github.com:YourUsername/your-repo.git (fetch)\norigin  git@github.com:YourUsername/your-repo.git (push)\n\n# 2. Add the original repository as the 'upstream' remote\ngit remote add upstream https://github.com/Original-Org/your-repo.git\n\n# 3. Verify the new remote was added\ngit remote -v\n\n### Terminal Output ###\norigin    git@github.com:YourUsername/your-repo.git (fetch)\norigin    git@github.com:YourUsername/your-repo.git (push)\nupstream  https://github.com/Original-Org/your-repo.git (fetch)\nupstream  https://github.com/Original-Org/your-repo.git (push)\n\n# 4. Now, before starting new work, sync your local 'main' with 'upstream'\ngit checkout main\ngit fetch upstream\ngit rebase upstream/main\ngit push origin main\n```\n\n\u003e By regularly rebasing your `main` branch from `upstream`, you ensure your fork never falls far behind.\n\nWhen you create new feature branches from your up-to-date `main`, they will be clean and have far fewer conflicts with the original project.\n\n## 11. How Do I Prove My Commits Really Came From Me? (Signing Commits)\nYou look at a commit history on GitHub and see a green “Verified” badge next to some commits. This badge indicates the commit was cryptographically signed by the author, proving it wasn’t forged by someone else.\n\n\u003e In high-security projects or professional open-source, signing your commits is a sign of good practice.\n\nIt ensures commit integrity and authorship. You can do this by generating a GPG key and telling Git to use it.\n\n```bash\n# 1. Generate a GPG key (follow the prompts)\ngpg --full-generate-key\n\n# 2. Find your GPG key ID\ngpg --list-secret-keys --keyid-format=long\n\n### Terminal Output ###\n/Users/you/.gnupg/pubring.kbx\n-----------------------------\nsec   ed25519/A1B2C3D4E5F6G7H8 2024-01-01 [SC]\n      ...\nuid                 [ultimate] Your Name \u003cyour_email@example.com\u003e\n...\n\n# The key ID is A1B2C3D4E5F6G7H8\n\n# 3. Tell Git to use this key to sign all your commits\ngit config --global user.signingkey A1B2C3D4E5F6G7H8\ngit config --global commit.gpgsign true\n\n# 4. Export your public key and add it to your GitHub/GitLab account\ngpg --armor --export A1B2C3D4E5F6G7H8\n# (Copy the output and paste it in your Git provider's SSH/GPG keys section)\n\n# Now, when you commit, it will be automatically signed.\ngit commit -m \"feat: Add secure user authentication\"\n\n# If you only want to sign a single commit, use the -S flag\ngit commit -S -m \"This is a particularly important commit\"\n```\n\n\u003e Now your commits will carry a digital signature that proves they came from you.\n\nIt’s a small step that adds a layer of trust and security to your team’s workflow.\n\n## 12. What is a “Detached HEAD” State and How Do I Fix It Without Losing My Work?\nYou run `git checkout \u003csome-old-commit-hash\u003e` just to look at some old code. Suddenly, your terminal warns you that you're in a **\"detached HEAD\"** state.\n\n\u003e It sounds scary, but it's not an error. It just means your `HEAD` (the current pointer) is pointing directly to a commit instead of a branch.\n\nThe danger is that if you start making commits here, they don’t belong to any branch and can get lost once you switch away.\n\nHere’s how to handle it:\n\n**Scenario A:** You were just looking and made no changes. Simply switch back to a branch. You’re done.\n\n```bash\n# Go back to your main development branch\ngit switch main\n```\n\n**Scenario B:** You made changes and want to keep them. Don’t panic! Just create a new branch right where you are. This will “attach” your HEAD to a new branch, saving all your work.\n\n```bash\n# You've made a couple of commits in a detached HEAD state...\n# Create a new branch to save them\ngit switch -c new-feature-from-old-state\n\n### Terminal Output ###\nSwitched to a new branch 'new-feature-from-old-state'\n\n# Your work is now safe on a regular branch!\n```\n\nThink of a detached HEAD as being in “read-only” mode. If you decide to write something, just give that work a name by creating a branch.\n\n## 13. Our Repo is Full of Large Assets, How Do We Handle Them Without Bloating the History?\nYour repository is getting slow. `git clone` takes time, and the `.git` directory is several gigabytes.\n\n\u003e Large binary files like datasets, design assets (`.psd`), or videos that have been committed directly.\n\nGit is designed for text, not large binaries. The solution is Git Large File Storage (LFS). It works by replacing large files in your repository with tiny text pointers. The actual files are stored on a separate server, so your repo stays lean and fast.\n\n```bash\n# 1. Install the LFS extension (a one-time setup on your machine)\ngit lfs install\n\n# 2. Tell LFS which file patterns to track.\n# This creates/updates the .gitattributes file.\ngit lfs track \"*.psd\"\ngit lfs track \"assets/videos/*.mp4\"\n\n# 3. Make sure .gitattributes itself is committed to the repo\ngit add .gitattributes\n\n# 4. Now, just work as you normally would.\n# Add, commit, and push your large files.\ngit add assets/designs/new-mockup.psd\ngit commit -m \"feat: Add new design mockup\"\ngit push origin main\n\n### Terminal Output ###\nUploading LFS objects: 100% (1/1), 150 MB / 150 MB, 30 MB/s\n...\n# (The push looks normal, but LFS is handling the big file in the background)\n```\n\n\u003e By using Git LFS, you get the versioning power of Git without the performance penalty of storing large files directly in the history.\n\n## 14. This Monorepo is Too Big. How Do I Split a Folder into a New Repository?\nYour company’s monorepo has grown unwieldy. The team has decided to extract the `mobile-app` directory into its own, separate repository.\n\nYou need to create this new repo while preserving the entire commit history for just the files in that directory.\n\n\u003e You can’t just copy-paste the files. You need to rewrite the history of the entire project to filter out everything *except* that folder.\n\nThis is a powerful, and potentially destructive, operation perfect for a tool like `git filter-repo`.\n\n**Warning: Always back up your repository before attempting this.**\n\n```bash\n# 1. Start with a fresh, bare clone of your monorepo.\ngit clone --bare https://github.com/my-org/monorepo.git\ncd monorepo.git\n\n# 2. Run git filter-repo to keep only the history of the 'mobile-app' folder.\n# This command rewrites history to make it seem like 'mobile-app' was always the root.\ngit filter-repo --path mobile-app/\n\n### Terminal Output ###\nParsed 10,000 commits\n...\nRepacked objects: 100% (5000/5000), done.\nNew history written in .git/filter-repo/analysis/...\n\n# 3. Your 'monorepo.git' directory is now a bare repo containing only the\n# history of the mobile-app. You can now push this to a new, empty repo.\ngit remote set-url origin https://github.com/my-org/new-mobile-app-repo.git\ngit push --mirror\n```\n\n\u003e This is a high-level architectural task that demonstrates a deep understanding of Git’s data model.\n\nBy carefully filtering history, you can perform complex repository refactoring, like splitting a monorepo or extracting a shared library, without losing the valuable historical context of your code.\n\n## 15. I Committed the Wrong Code, How Do I Undo My Error?\nWhile pushing multiple commits to the repo, it’s pretty common for developers to accidentally include something we didn’t mean to maybe a broken function, an unfinished component, or debug logs that slipped through.\n\n\u003e You commit… push… and then realize **Wait, that shouldn’t have gone in.**\n\nIf it was your most recent commit, you can easily undo it using one of the following commands based on what you want to keep.\n\n```bash\n# Remove the last commit but keep the code staged\ngit reset --soft HEAD~1\n\n### Terminal Output ###\n$ git reset --soft HEAD~1\n$ git status\nOn branch dev\nChanges to be committed:\n  (use \"git reset HEAD \u003cfile\u003e...\" to unstage)\n modified:   app/component/LoginForm.js\n modified:   utils/api.js\n```\n\nThe above is the most common thing a developer do, but there are some other parameters too…\n\nLike:\n*   `--soft` → Keeps your code staged (ready to recommit)\n*   `--mixed` → Keeps the code but **unstaged**, so you can review or edit before committing again\n*   `--hard` → Wipes out both the commit **and** the changes (use only if you're sure)\n*   `--keep` → Keeps local changes **only if** they don’t conflict with the reset target\n*   `--merge` → Useful during merge conflicts; resets to commit but keeps unmerged changes\n\n\u003e For day-to-day mistakes, `--soft` and `--mixed` are usually enough.\n\n## 16. When My Work Collided with a Teammate Changes, Now I Can’t Pull?\nYou’re working on your branch, pushing out features, fixing bugs… and then you try to `git pull` to grab the latest updates from the repo.\n\nBut instead of smoothly updating, Git gives you that dreaded merge conflict message.\n\nBecause while you were editing `app/component/LoginForm.js`, one of your teammates also made changes to that *same file* in the *same branch* and already pushed their version. Now Git doesn’t know whose version should win, yours or theirs, so it refuses to proceed until you decide.\n\n\u003e The safest approach here (especially if you’re not ready to merge yet) is to **stash your local changes**, pull the latest updates, and then reapply your work on top.\n\nHere’s how developers usually handle it:\n\n```bash\n# Save your uncommitted changes into a stash (-u = include untracked files)\ngit stash save \"login-form-update\" -u\n\n#############################\nSaved working directory and index state WIP on dev: 3a4b8f7 login form tweaks\n#############################\n\n# Pull the latest changes from the remote branch\ngit pull\n\n#############################\nUpdating 3a4b8f7..b9c2d1e\nFast-forward\n app/component/LoginForm.js | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n#############################\n\n# See the list of all stashes saved so far\ngit stash list\n\n#############################\nstash@{0}: WIP on dev: 3a4b8f7 login form tweaks\nstash@{1}: WIP on feature-login: 9f8a7b3 API integration\n#############################\n\n# Apply a specific stash back to your working directory (n = stash number)\ngit stash apply stash@{0}\n\n#############################\nOn branch dev\nChanges to be committed:\n  (use \"git reset HEAD \u003cfile\u003e...\" to unstage)\n modified:   app/component/LoginForm.js\n#############################\n```\n\nBy stashing first, you avoid a messy manual merge right away, and you ensure your teammate’s updates come in cleanly before layering your work back in.\n\n## 17. Why Did Applying My Stash Cause Conflicts?\nSometimes after applying a stash, you’ll see a scary list of merge conflicts. This usually happens if the same parts of your file have been changed both in **your stashed code** and in the **pulled changes** from the remote branch.\n\n\u003e If there are changes in the same region of code, conflicts are **expected** and you’ll have to resolve them manually.\n\n```bash\n# Apply a stash that conflicts with the current code\ngit stash apply stash@{0}\n\n### Terminal Output ###\nAuto-merging app/component/LoginForm.js\nCONFLICT (content): Merge conflict in app/component/LoginForm.js\nerror: could not apply 7c3b9d1... login form tweaks\n```\n\nHere’s what it might look like in VS Code (or any editor that supports Git conflict markers):\n\n![Conflicts shown in VS Code](https://miro.medium.com/v2/resize:fit:875/1*o8bLYUmgMw-qsEYPlliD_g.png)\n\n*Conflicts shown from [sadanandpai](https://github.com/sadanandpai)*\n\nThe built-in merge editor of VSCode is the common useful tool for developers, it shows **Current Change** (remote), **Incoming Change** (yours), and allows you to click **Accept Current**, **Accept Incoming**, or **Accept Both**.\n\n## 18. I Just Committed but Forgot to Add a Few Files?\nIt happens way too often you hit `git commit`, push out your changes, and then realize you completely forgot to add that one important file… or maybe two.\n\nYou don’t have to make a brand-new commit just for those. You can **amend** the last commit to include your missed changes (or even update the commit message).\n\n```bash\n# Stage the missing files\ngit add utils/helpers.js app/config.js\n\n# Update the last commit but keep the same commit message\ngit commit --amend --no-edit\n\n### Terminal Output ###\n[dev 9a8b7c6] Fix login bug\n Date: Fri Aug 9 12:45:00 2025 +0500\n 2 files changed, 12 insertions(+)\n\n# Or update both the code and the commit message\ngit commit --amend -m \"Fix login bug and add missing config\"\n\n### Terminal Output ###\n[dev 4f3c2d1] Fix login bug and add missing config\n Date: Fri Aug 9 12:46:10 2025 +0500\n 2 files changed, 12 insertions(+)\n```\n\n`--no-edit` is just there to skip the step where Git asks you to retype the commit message. If you only want to **change the message** without touching the code, just skip `git add` and run:\n\n```bash\ngit commit --amend -m \"My updated commit message\"\n```\n\nAmend is safe if you haven’t pushed yet but if you **already pushed**, you will need to force push (`git push --force-with-lease`) which can affect others, so be careful in team repos.\n\n## 19. My Branch Name Doesn’t Match the Work Anymore?\nIt’s pretty common to start a branch with one idea in mind maybe `feature-user-login` but as you work, the scope changes. Suddenly you are also fixing signup bugs, updating session handling, and touching half the auth system.\n Now the branch name doesn’t really tell the team what it’s doing anymore. Renaming it avoids confusion in pull requests and keeps your team’s Git history meaningful.\n\nHere’s how to rename it both locally and on the remote:\n\n```bash\n# Step 1: Make sure you're on the branch you want to rename\ngit checkout old-branch-name\n\n### Terminal Output ###\nSwitched to branch 'old-branch-name'\n\n# Step 2: Rename the branch locally\ngit branch -m new-branch-name\n\n### Terminal Output ###\n# (No output, just renames silently)\n\n# Step 3: Push the renamed branch to remote\ngit push origin new-branch-name\n\n### Terminal Output ###\nTotal 0 (delta 0), reused 0 (delta 0), pack-reused 0\nremote:\nremote: Create a pull request for 'new-branch-name' on GitHub by visiting:\nremote:   https://github.com/your-org/your-repo/pull/new/new-branch-name\nremote:\n * [new branch]      new-branch-name -\u003e new-branch-name\n\n# Step 4: Delete the old branch from remote\ngit push origin :old-branch-name\n\n### Terminal Output ###\nTo github.com:your-org/your-repo.git\n - [deleted]         old-branch-name\n\n# Step 5: Set the new branch to track the remote branch\ngit push --set-upstream origin new-branch-name\n\n### Terminal Output ###\nBranch 'new-branch-name' set up to track remote branch 'new-branch-name' from 'origin'.\n```\n\n\u003e Renaming keeps the Git log cleaner, prevents misleading PR titles, and avoids the “Wait… what’s this branch actually for?” problem in team discussions.\n\n## 20. You Already Pushed Your Commit, But Now You Need to Update It?\nSometimes you push your changes and feel good about it… until you realize you forgot a file, made a small typo, or the commit message wasn’t exactly what you wanted.\n\nIf it’s a local commit, life’s easy but if you’ve **already pushed it**, you can still update it.\n\n\u003e You’ll need to **force push** afterward, so you should be careful when working in a team branch.\n\nHere’s how you can update a pushed commit:\n\n```bash\n# Stage the missing or updated files\ngit add utils/helpers.js app/config.js\n\n### Terminal Output ###\n# (No output, files are staged silently)\n\n# Update the last commit but keep the existing message\ngit commit --amend --no-edit\n\n### Terminal Output ###\n[dev 7f4c2a1] Fix login bug and add missing config\n Date: Fri Aug 9 14:05:00 2025 +0500\n 2 files changed, 12 insertions(+)\n\n# Push the updated commit, overwriting the remote\ngit push --force-with-lease\n\n### Terminal Output ###\nTotal 0 (delta 0), reused 0 (delta 0), pack-reused 0\nTo github.com:your-org/your-repo.git\n + 3b5a6c1...7f4c2a1 dev -\u003e dev (forced update)\n```\n\n\u003e Use `--force-with-lease` instead of plain `--force`\n\nIt’s safer because it checks if the remote branch has moved before overwriting it, reducing the risk of wiping out someone else’s work.\n\n\u003e But If someone branched off or committed to the same branch after your push, force pushing could overwrite their work.\n\nMake sure you coordinate with your teammates before doing this.\n\n## 21. My Push Rejected, I Then Pulled… and Now Conflicts Everywhere!\nNormally, when working with teams of many developers on a single project, it’s common for multiple people to be pushing to the same branch within minutes (or seconds) of each other.\n\nYou finish your commits, feeling ready to push but Git suddenly stops you with:\n\n```bash\n! [rejected]        dev -\u003e dev (fetch first)\nerror: failed to push some refs to 'origin/dev'\nhint: Updates were rejected because the remote contains work that you do not have locally.\n```\n\nThis means the **remote branch is ahead of your local branch**, usually because a teammate has already pushed changes you don’t have yet.\n\nYou can use `git pull` if there’s no overlap in changes, Git will merge automatically.\n\n\u003e But If both you and a teammate touched the same part of the same file, Git will stop with a **merge conflict.**\n\nResolve conflicts in your editor (look for the `\u003c\u003c\u003c\u003c\u003c\u003c\u003c`, `=======`, and `\u003e\u003e\u003e\u003e\u003e\u003e\u003e` markers), but **If Things Get Messy**, maybe you pulled too early, or the conflict resolution is too tangled don’t panic:\n\n```bash\ngit merge --abort\n```\n\nThis will rewind your repo to the state before the pull so you can retry later.\n\n\u003e When working in a busy branch, it’s normal to see push rejections.\n\nThe key is to **pull first**, merge carefully, and only push once your branch is fully up to date with the remote. If conflicts appear, handle them immediately before they grow into bigger headaches.\n\n## 22. Push Rejected Again? This Time Let’s Rebase Instead of Merging\nSometimes when working on a busy branch, you’ll hit that same dreaded **push rejected** message but maybe you don’t want Git to create a merge commit just to pull in your teammate’s changes.\n\n\u003e Instead, you want your commits to be placed **on top of the latest remote code**, as if you had written them after your teammate’s work in the first place.\n\nThat’s exactly what **rebase** does. If there are no conflicts, then using `git pull --rebase`, Git will simply replay your commits one-by-one on top of the latest remote changes:\n\n```bash\nFirst, rewinding head to replay your work on top of it...\nApplying: Add login validation\nApplying: Update API config\n```\n\nThen you can push the changes, but if you and a teammate edited the same part of a file, Git will stop mid-rebase:\n\nJust like in a merge, open the file, fix the conflict markers, then:\n\n```bash\n# Stage resolved files\ngit add app/component/LoginForm.js\n\n# Continue rebase\ngit rebase --continue\n```\n\nOnce all commits are replayed, push again:\n\n```perl\ngit push\n```\n\nIf things feel messy or you made a wrong move:\n\n```css\ngit rebase --abort\n```\n\nThis will put your branch back to the exact state it was before starting the rebase.\n\n\u003e Rebase is cleaner because it avoids merge commits and keeps history linear but be careful when rebasing shared branches, as rewriting history can disrupt others.\n\n## 23. My PR Shows Conflicts, How Do I Fix It Before the Team Yells at Me?\nYou finally finish your feature, tests are passing, and you open a Pull Request (PR) to merge into `main`.\n\n![Conflicting Message](https://miro.medium.com/v2/resize:fit:739/1*BJqxLlL0D-RDAwuxg1Fg_g.png)\n\n\u003e You’re expecting that sweet green “Able to merge” check mark… but instead, Git slaps you with:\n\nWell, while you were coding away in your branch, your teammates weren’t just sitting idle. They merged changes into `main` that touch the same files—or even the exact same lines you worked on.\n\n\u003e Now your PR is stuck because Git doesn’t know whose changes to trust without your input.\n\nYou have to bring your branch up to date with `main` and resolve the conflicts.\n\nYou have **two ways** to do this\n1.  **Merge**\n2.  **Rebase**\n\nPick **one** approach. Don’t mix them in the same branch unless you like chaos.\n\nIf your branch is `develop` and the source branch is `main`:\n\n```bash\n# 1. Switch to main\ngit checkout main\n\n# 2. Pull the latest changes\ngit pull\n\n# 3. Switch back to your branch\ngit checkout develop\n\n# 4. Merge main into your branch\ngit merge main\n```\n\nIf conflicts appear:\n*   Open each file Git flagged\n*   Look for `\u003c\u003c\u003c\u003c\u003c\u003c\u003c`, `=======`, and `\u003e\u003e\u003e\u003e\u003e\u003e\u003e` markers\n*   Keep your changes, their changes, or combine both\n\nOnce resolved:\n\n```bash\n# Stage resolved files\ngit add \u003cfiles\u003e\n\n# Continue the merge if still in progress\ngit merge --continue\n\n# Push your branch\ngit push\n```\n\nIf you like a clean commit history with no merge commits (REBASE):\n\n```bash\n# 1. Switch to main\ngit checkout main\n\n# 2. Pull the latest changes\ngit pull\n\n# 3. Switch back to your branch\ngit checkout develop\n\n# 4. Rebase your branch onto main\ngit rebase main\n```\n\nIf conflicts appear, fix them exactly as in the merge approach, then:\n\n```bash\n# Stage resolved files\ngit add \u003cfiles\u003e\n\n# Continue the rebase\ngit rebase --continue\n```\n\nYou might have to repeat `git rebase --continue` multiple times if each commit has conflicts.\n\nFinally, because rebase rewrites history `git push -f` .\n\n\u003e But Merge or Rebase which is better?\n\n*   **Merge** → Best for shared branches, keeps a full record of merges, safer for team workflows.\n*   **Rebase** → Best for keeping history clean, but only safe when you’re the sole owner of the branch.\n\n## 24. I Have Too Many Commits!\nYou’ve been working on a feature for a few days, committing as you go:\n*   *“Fix typo”*\n*   *“Adjust padding”*\n*   *“Okay now really fix the typo”*\n*   *“Forgot to import the thing”*\n\nNow your PR looks like a messy diary instead of clean, intentional commits. The reviewer doesn’t need to see every little fix they just want one neat commit that says what this feature does.\n\nThat’s where **squashing** comes in. Squashing means **combining multiple commits into a single one**, so your branch history looks clean and professional.\n\n\u003e The best way is to use Git **interactive rebase** and yes, VS Code makes it much friendlier than staring at a terminal-only interface.\n\nLet’s say you want to squash your **last N (5) commits** into one:\n\n```bash\n# Replace \u003cn\u003e with the number of commits you want to squash\ngit rebase -i HEAD~\u003cn\u003e\n\n# For 5\ngit rebase -i HEAD~5\n```\n\nIn the Editor VS Code, Git will open a file listing your last N commits, something like:\n\n![Git Rebase view](https://miro.medium.com/v2/resize:fit:875/1*HUDfkvgN61uHVmmb5jCBUA.png)\n\n![Git Rebase view after editing](https://miro.medium.com/v2/resize:fit:875/1*Rj4wR922wW4oznhxzQFOyg.png)\n\nSince squashing rewrites commit history, you’ll need to force push `git push -f`.\n\n\u003e Make sure Squash before opening your PR to keep the history clean.\n\n## 25. I Committed to One Branch but Need the Same Changes in Another\nYou’ve just finished coding a neat little fix or feature, committed it to your current branch… and then realize you were supposed to make that change in another branch instead.\n\nMaybe it needs to go into `main` for an urgent hotfix, or maybe the work belongs in `develop` rather than your feature branch.\n\n\u003e No need to retype or copy-paste everything Git has your back with **cherry-pick**.\n\nCherry-picking lets you take a commit from one branch and apply it to another, as if you wrote it there in the first place.\n\nFirst, find the **commit ID** (hash) of the commit you want to copy. You can see it by running:\n\n```bash\ngit log\n```\n\nThen simply:\n\n```bash\n# Switch to the branch where you want the commit applied\ngit checkout \u003cdestination-branch\u003e\n\n# Apply the commit by its ID\ngit cherry-pick \u003ccommit-id\u003e\n```\n\nThe commit will now appear in your destination branch with the same changes and commit message.\n\n## 26. I Merged My Changes… Then Realized They Shouldn’t Be There\nYou have pushed your changes, your PR got merged into `main`, and you’re feeling good until someone points out a bug, or you realize those changes weren’t supposed to go live yet. Panic mode? Not necessary.\n\nInstead of deleting history or force-pushing (which can break things for everyone), you can **revert** the commit.\n\n\u003e Revert doesn’t erase the commit from history it simply creates a **new commit** that undoes the changes from the one you specify.\n\nFirst, find the commit ID you want to revert:\n\n```bash\ngit log\n\n# Create a new commit that reverses the specified commit\ngit revert \u003ccommit-id\u003e\n\n# Push the new commit to remote\ngit push\n```\n\nNow the unwanted changes are rolled back, but the history stays intact everyone can still see that the original commit happened and that it was intentionally reverted.\n\nIf you need to revert a merge commit, add the `-m 1` option to tell Git which parent branch to treat as the main line of history:\n\n```bash\ngit revert -m 1 \u003cmerge-commit-id\u003e\n```\n\n\u003e This is safer for team projects because it keeps the history clean and avoids rewriting anything that’s already been shared.\n\nIn case you enjoy this blog, feel free to [follow me on Medium](https://medium.com/@fareedkhandev) I only write there.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffareedkhan-dev%2Fgit-workflow-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffareedkhan-dev%2Fgit-workflow-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffareedkhan-dev%2Fgit-workflow-guide/lists"}