Skip to content
All question banks

Web Development

Git & GitHub Questions

Comprehensive guide to Version Control basics, core Git commands, branching strategies, and remote synchronization. Essential for developers and DevOps engineers.

200 of 200 questions

Learn the Basics15

Version Control is a system that records changes to a file or set of files over time so that you can recall specific versions later. It allows multiple people to work on the same project simultaneously, tracks who made which changes, and provides a safety net to revert to previous stable states if errors occur.

Version control is essential for managing source code because it prevents data loss, enables collaboration without overwriting others' work, and maintains a complete history of project evolution. It allows developers to experiment in branches and provides a clear audit trail of why specific changes were implemented through commit messages.

Git is a distributed version control system (DVCS) designed to handle everything from small to very large projects with speed and efficiency. Unlike older systems, every Git directory on every computer is a full-fledged repository with complete history and full version-tracking capabilities, independent of network access or a central server.

Git is the local command-line tool that manages version control and tracks history on your machine. GitHub is a cloud-based hosting service that lets you manage Git repositories online. While Git is used for the actual versioning logic, GitHub adds a social layer, project management tools, and a central location for teams to collaborate.

Git is distributed, meaning every user has a full copy of the project history, whereas SVN (Subversion) is centralized, requiring a server connection for most operations. Compared to Mercurial, Git is generally faster and offers more powerful branching and merging capabilities, though it has a steeper learning curve for beginners.

Git can be installed on Windows using the Git for Windows installer (Git Bash). On macOS, it can be installed via Homebrew using 'brew install git' or through Xcode Command Line Tools. On Linux, it is typically installed using a package manager like 'sudo apt install git' for Debian/Ubuntu systems.

The 'git init' command creates a new, empty Git repository or reinitializes an existing one. It creates a hidden '.git' subdirectory in your current working folder, which contains all the necessary metadata, object databases, and configuration files needed to start tracking changes in that directory.

The 'git config' command is a tool used to set and query configuration variables that control all aspects of how Git looks and operates. These variables can be stored in three different levels: system (all users), global (current user), and local (specific repository), allowing for highly customized environments.

Global configuration applies to all repositories for the current user on the system, typically stored in the user's home directory. Local configuration is stored within the '.git/config' file of a specific repository and overrides global settings, which is useful for using different identities for work and personal projects.

You set your identity using the commands 'git config --global user.name "Your Name"' and 'git config --global user.email "email@example.com"'. This information is attached to every commit you make, identifying you as the author of the changes and ensuring proper attribution in collaborative environments.

A Git repository is a digital folder that stores the files, history, and configuration of a project. It keeps track of every change made to the files through a series of 'snapshots' called commits. It essentially functions as a database for your project's entire lifecycle and version history.

A local repository resides on your personal computer, where you perform work, create branches, and make commits. A remote repository is hosted on a server or service like GitHub, acting as a central hub where team members push their changes to share them or pull changes to update their local versions.

The Working Directory is the actual folder on your computer's file system where the project files currently reside and where you make edits. These files are 'checked out' from the Git database and placed on your disk so you can modify them using your preferred text editor or IDE.

The Staging Area, also called the Index, is a file maintained by Git that contains information about what will go into your next commit. It acts as a buffer between your working directory and your repository, allowing you to selectively choose which changes should be bundled together into a single version snapshot.

The '.git' folder is a hidden directory created at the root of your project when you initialize a repository. It contains the entirety of the project's history, the object database (blobs, trees, commits), the HEAD pointer, configuration settings, and all metadata required for Git to function properly.

Basic Git Commands15

The 'git status' command displays the state of the working directory and the staging area. It shows which changes have been staged, which haven't, and which files aren't being tracked by Git. It is the most common command used to understand what Git is currently tracking and what's ready to be committed.

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. This command marks the file as 'staged', moving it from the working state into the index for the next snapshot.

'git add .' stages all new and modified files in the current directory and subdirectories, but in older versions of Git, it might ignore deleted files. 'git add -A' (or --all) stages everything including new files, modified files, and deleted files across the entire working tree, regardless of your current path.

A commit is a snapshot of your project's staged changes at a specific point in time. When you run 'git commit', Git saves the contents of the staging area into a new object in the database and updates the project history. Every commit is identified by a unique SHA-1 hash and includes an author message.

'git commit -m' allows you to provide a commit message directly in the command line for already staged files. 'git commit -am' is a shortcut that automatically stages all tracked, modified files and commits them with a message in one step, but it will not include any new (untracked) files.

The 'git log' command shows the commit history for the current branch, listing commits in reverse chronological order. Useful options include '--oneline' for a compact view, '--graph' to see branching structures, '--author' to filter by user, and '--patch' to see the actual code changes introduced in each commit.

The 'git diff' command shows the differences between various states of your project. By default, it shows the changes in your working directory that haven't been staged yet. It is used to review exactly what lines of code were added, removed, or modified before deciding to stage them for a commit.

'git diff' compares the working directory with the staging area (unstaged changes). 'git diff --staged' (or --cached) compares the staging area with the last commit (HEAD). Using both commands allows you to precisely track what you are currently editing versus what you have already prepared to commit.

'.gitignore' is a text file that tells Git which files or directories to ignore and not track. It is useful for excluding build artifacts (like /bin or /node_modules), sensitive configuration files (like .env), and temporary system files, ensuring the repository remains clean and free of unnecessary data.

If a file is already being tracked, adding it to '.gitignore' will not stop Git from tracking it. You must first remove it from the index using 'git rm --cached <file>'. This keeps the file in your working directory but stops Git from versioning it, allowing the ignore rules to take effect.

'git rm' is used to remove files from the working tree and from the Git index (staging area). Unlike the standard system 'rm' command, 'git rm' ensures that the removal is tracked as a change in the repository, so the file will no longer appear in future snapshots after a commit.

The standard 'rm' command only deletes the file from your local disk (working directory), leaving Git thinking the file is 'deleted' but still tracked. 'git rm' deletes the file from disk AND stages the deletion in Git's index, making it a complete operation for the next commit.

The 'git mv' command is used to move or rename a file, directory, or symlink. While Git can often detect renames automatically, using 'git mv' explicitly renames the file on disk and stages the change in the index simultaneously, providing a cleaner way to reorganize project structures.

Commit history is primarily viewed using 'git log'. For a visual representation of how different branches have merged over time, 'git log --graph --oneline --all' is highly effective. Additionally, tools like 'gitk' or integration in IDEs (like VS Code's GitLens) provide a GUI for browsing past versions.

HEAD is a symbolic reference that points to the current checkout branch or commit you are working on. In most cases, it points to the tip of your current branch. When you make a commit, HEAD moves forward to the new snapshot, representing the most recent state of your active development.

Branching Basics15

A branch in Git is essentially a lightweight, movable pointer to one of the commits in your repository. It allows you to diverge from the main line of development to work on new features or bug fixes in isolation without affecting the stable code, which can later be merged back.

Branches are used to manage different versions of a project simultaneously. They enable 'feature-based development' where each task has its own sandbox. This prevents unstable experimental code from breaking the production version and allows multiple team members to work on conflicting parts of the code without interference.

The traditional default branch name in Git was 'master'. However, modern industry standards, including GitHub and the Git project itself, have shifted to using 'main' as the default branch name for new repositories to promote more inclusive and modern terminology in the software development community.

The 'git branch' command is used to list, create, or delete branches. When run without arguments, it shows a list of all local branches and highlights the one you are currently on. It is the primary tool for managing the structural organization of your different development paths.

You can create a new branch using 'git branch <branch-name>'. This creates a new pointer at the current commit but does not switch you to it. To both create and switch to a new branch immediately, the more common command used is 'git checkout -b <branch-name>'.

The 'git checkout' command is used to switch between branches or restore working tree files. When switching branches, it updates the files in your working directory to match the version stored in that branch and moves the HEAD pointer to the new branch's tip.

'git checkout' is a versatile command that can switch branches, restore files, and handle detached HEADs. 'git switch' was introduced in Git 2.23 as a more focused command specifically for changing branches, aiming to reduce the confusion caused by 'checkout' having too many unrelated responsibilities.

The most common way is using 'git checkout -b <branch-name>'. Alternatively, using the newer, more specific command, you can run 'git switch -c <branch-name>'. Both commands create a new branch from the current HEAD and immediately move your working context to that new branch.

'git branch -d' is a 'safe' delete that only removes the branch if it has been fully merged into its upstream or current branch. 'git branch -D' is a 'forced' delete that removes the branch regardless of its merge status, which is useful for discarding experimental work.

To rename the current branch you are on, use 'git branch -m <new-name>'. If you want to rename a different branch while staying on your current one, use 'git branch -m <old-name> <new-name>'. If the branch has already been pushed, additional steps are required to update the remote.

You delete a local branch using 'git branch -d <branch-name>'. To delete a remote branch from a server like GitHub, you use the command 'git push origin --delete <branch-name>'. Note that you cannot delete a branch while you are currently working on it; you must switch away first.

The 'git merge' command integrates changes from another branch into your current active branch. It finds the common base commit between the two branches and creates a new 'merge commit' (if necessary) that combines the work from both lines of development into a single unified state.

A Fast-forward merge occurs when the destination branch has no new commits since the split; Git simply moves the pointer forward. A 3-way merge occurs when both branches have diverged; Git uses a common ancestor and the two branch tips to create a new, combined 'merge commit'.

A merge conflict occurs when Git cannot automatically determine how to combine changes because two branches have modified the same line of the same file, or one branch deleted a file that another branch modified. Git stops the merge process and asks the user to manually resolve the differences.

To resolve a conflict, you open the affected files, which contain Git markers (<<<<<<<, =======, >>>>>>>). You manually edit the code to the desired final version, remove the markers, save the file, and then run 'git add' followed by 'git commit' to finalize the merge process.

Git Remotes15

A remote repository is a version of your project that is hosted on the internet or another network. It allows multiple people to collaborate on the same code by providing a centralized synchronization point. Common platforms for hosting remotes include GitHub, GitLab, and Bitbucket.

The 'git remote' command allows you to manage the set of tracked remote repositories. Running 'git remote -v' shows you the URLs of the remotes you have connected to your local repository, identifying where your data will be sent (push) or retrieved from (fetch/pull).

You add a remote by using the command 'git remote add <name> <url>'. By convention, the primary remote is named 'origin'. This creates a shortcut name for a long URL, making it much easier to synchronize your local work with a hosted version of the project.

In Git, 'origin' is the default name given to the remote repository from which a project was originally cloned. It is just a conventional alias for the remote URL. While you can name your remotes anything, using 'origin' is a standard practice that most Git commands and developers expect.

The 'git clone <url>' command creates a local copy of a remote repository. It doesn't just download the files; it downloads the entire project history, all branches, and sets up a remote called 'origin' pointing back to the source URL so you can immediately start collaborating.

Cloning creates a local copy of a repository on your machine, typically one you have write access to. Forking is a GitHub feature that creates a personal server-side copy of someone else's project under your account, allowing you to make changes independently without affecting the original repository.

The 'git fetch' command downloads all the latest commits, branches, and tags from a remote repository but does not merge them into your local working branches. It allows you to see what others have worked on without changing your current local state, essentially updating your remote-tracking branches.

The 'git pull' command is a combination of two steps: 'git fetch' followed by 'git merge'. It downloads changes from the remote server and immediately attempts to integrate them into your current local branch. While convenient, it can lead to unexpected merge conflicts if your local work has diverged.

The primary difference is that 'fetch' only downloads remote data without modifying your local working files, making it safer for inspection. 'pull' is more aggressive, as it both downloads the data and immediately tries to merge it into your active branch, potentially altering your current work-in-progress.

The 'git push' command uploads your local repository commits to a remote repository. This is how you share your progress with the rest of the team. Git typically prevents a push if it results in a non-fast-forward merge on the server, forcing you to pull and resolve conflicts first.

The '-u' (or --set-upstream) flag links your local branch to the remote branch. Once this association is established, Git remembers the relationship, allowing you to use shorthand commands like 'git push' or 'git pull' without specifying the remote name or branch name in the future.

The '--force' flag overrides the remote history with your local history, even if it causes data loss on the server. It should only be used in rare cases, such as when you've accidentally pushed sensitive information and cleared it locally, or when working on a private feature branch alone.

An upstream branch is a default remote branch that your local branch is configured to track. It serves as the reference point for synchronization; when you run pull or push without arguments, Git looks at the upstream configuration to know exactly which remote server and branch to interact with.

You can set an upstream branch using 'git push -u origin <branch_name>' during your first push. If the branch already exists on the remote, you can manually set it using 'git branch --set-upstream-to=origin/<branch_name> <local_branch_name>', which establishes the tracking relationship for future synchronization.

To delete a branch from a remote server like GitHub, you use the command 'git push <remote_name> --delete <branch_name>'. This informs the server to remove the branch pointer and its associated history, provided no other branches are depending on those specific commits.

GitHub Essentials13

GitHub is a cloud-based platform that hosts Git repositories and provides a suite of collaboration tools. It includes features like issue tracking, pull requests, automated CI/CD (GitHub Actions), and social networking for developers, making it the central hub for open-source and enterprise software development worldwide.

To create a repository, click the '+' icon in the top-right corner of GitHub and select 'New repository'. You must provide a name, choose visibility (public/private), and can optionally initialize it with a README, .gitignore, and a license file before clicking 'Create repository'.

Public repositories are visible to everyone on the internet, allowing anyone to view the code and fork the project. Private repositories are only accessible to the owner and designated collaborators, making them suitable for proprietary commercial projects or sensitive internal tools.

A README file (README.md) is the first file users see when they visit a repository. It is important because it provides a project overview, installation instructions, usage examples, and contribution guidelines. It acts as the primary documentation and 'front door' for any software project.

Markdown is a lightweight markup language used to format text in README files, issues, and pull requests on GitHub. It uses simple symbols (like # for headers, * for bullets, and [ ] for links) to create rich text that is easy to read in both plain text and rendered HTML formats.

A good README should include a clear project title, a concise description, prerequisites, installation steps, usage instructions, screenshots or GIFs of the software in action, a list of technologies used, and a clear license. It should be organized using headers and formatted with Markdown for readability.

GitHub Issues are a built-in task tracking system for repositories. They are used to report bugs, suggest new features, or track project milestones. Issues support labels, milestones, assignees, and threaded comments, making them an essential tool for project management and community feedback.

A Pull Request is a proposal to merge a set of changes from one branch into another (usually into the main branch). It provides a dedicated interface for team members to review code, discuss changes, run automated tests, and suggest improvements before the code is officially integrated.

There is no functional difference; they refer to the same concept of proposing code changes. 'Pull Request' is the terminology used by GitHub and Bitbucket, while 'Merge Request' is the terminology used by GitLab. Both facilitate the same code review and integration workflow.

After pushing a feature branch to GitHub, navigate to the repository and click the 'Compare & pull request' button. Fill in the title and description explaining the changes, select the base and compare branches, and click 'Create pull request' to notify reviewers and start the review process.

Best practices include keeping PRs small and focused on a single task, writing a descriptive title and summary, linking related issues, ensuring all tests pass, and being responsive to reviewer feedback. This ensures a faster review cycle and maintains high code quality throughout the project history.

Code review is the process where team members examine each other's code changes in a Pull Request. The goal is to identify bugs, ensure consistency with coding standards, share knowledge across the team, and improve the overall design and maintainability of the software before it is merged.

Labels are color-coded tags used to categorize and filter items in GitHub. Common labels include 'bug', 'enhancement', 'help wanted', and 'documentation'. By applying labels, teams can quickly organize their workload, prioritize tasks, and help contributors find issues they are capable of solving.

Collaboration on GitHub15

Forking is the process of creating a personal copy of another user's repository on your own GitHub account. This is the standard way to contribute to open-source projects where you don't have direct write access, allowing you to experiment freely and then submit changes via a Pull Request.

A fork is a server-side copy on GitHub that establishes a link to the original project for easy Pull Requests. A clone is a local copy on your physical computer used for editing. Typically, you fork a project first on GitHub and then clone your fork to your local machine to work.

To fork a repository, navigate to the target project on GitHub and click the 'Fork' button in the top-right corner of the page. Select your personal account or an organization as the destination. GitHub then creates an identical copy of the repository under your namespace.

This is a Pull Request where the changes originate from a branch in your forked repository and are proposed for integration into the original 'upstream' repository. GitHub automatically recognizes the relationship between the two and allows you to initiate the PR across different repository owners.

To sync a fork, you add the original repository as a remote named 'upstream'. You run 'git fetch upstream' to get the latest changes, then 'git merge upstream/main' into your local main branch. Finally, you 'git push' to update your fork on GitHub, keeping it current with the source.

Collaborators are specific users who have been granted direct read and write access to a repository. Unlike contributors who use the fork-and-PR model, collaborators can push directly to branches, manage issues, and merge Pull Requests, which is the standard model for internal team projects.

Navigate to the repository 'Settings', click on 'Collaborators' in the left sidebar, and then click 'Add people'. You can search for users by their GitHub username or email. Once they accept the invitation, they will have the permissions necessary to contribute directly to the project.

Mentions are a way to notify specific users or teams by typing '@' followed by their username in comments, issues, or Pull Requests. This triggers a notification for that person, drawing their attention to a specific discussion, request for review, or task that requires their input.

Reactions are emoji-based responses (like 👍, ❤️, or 🎉) that can be added to comments, issues, and Pull Requests. They provide a quick, lightweight way to express agreement, appreciation, or acknowledgment without adding a separate text comment, helping to keep discussions clean and concise.

GitHub allows users to leave comments on specific lines of code within a Pull Request or commit. This 'line-level' commenting is crucial for code reviews, as it allows reviewers to provide precise feedback, ask questions, or suggest improvements exactly where the code is located.

Saved replies are a GitHub feature that allows you to create reusable templates for comments you use frequently, such as 'Thanks for the contribution!' or 'Please add tests.' This saves time and ensures consistency when managing large numbers of issues or Pull Requests in popular repositories.

GitHub Discussions is a collaborative forum for a repository, separate from issues. While issues are for tracking tasks and bugs, Discussions are for open-ended questions, brainstorming, announcements, and community support, providing a space for conversation that doesn't necessarily result in a code change.

A branch naming convention is a standard for naming branches to improve team organization. Common patterns include prefixing the branch type, such as 'feat/login-page', 'bugfix/header-overlap', or 'chore/update-deps'. This makes it immediately clear what the purpose of a branch is when looking at the list.

Good commit messages should have a short summary (50 chars), followed by a blank line and a detailed description if needed. Use the imperative mood (e.g., 'Fix bug' instead of 'Fixed bug'), capitalize the first letter, and do not end with a period. This makes history logs much easier to read.

Conventional Commits is a specification for adding semantic meaning to commit messages. It uses a structured format like 'type(scope): description', where type is 'feat', 'fix', 'refactor', etc. This allows for automated changelog generation and simplified versioning tools to understand the nature of the changes.

Merge Strategies10

A Fast-Forward merge happens when the base branch has no new commits since you branched off. Git simply moves the base branch pointer forward to the tip of your feature branch. This results in a linear history without a separate 'merge commit' object being created in the repository.

A Non-Fast-Forward merge occurs when both the base branch and the feature branch have diverged (both have new, unique commits). Git creates a new 'merge commit' that has two parent commits, effectively tying the two separate lines of development back together into a single history path.

The '--no-ff' flag forces Git to create a merge commit even if a fast-forward merge is possible. This is used to preserve the historical information that a feature branch existed and to group all the commits related to that feature together under a single integration point.

A Squash merge takes all the commits from a feature branch and combines (squashes) them into a single commit before merging it into the base branch. This keeps the main branch history very clean and linear by hiding all the 'in-progress' or 'fixup' commits from the feature branch.

Rebase is the process of moving or combining a sequence of commits to a new base commit. Instead of a merge commit, it takes your changes and 're-plays' them on top of the latest version of the target branch, resulting in a perfectly linear project history without merge artifacts.

Merge creates a new commit that joins two histories, preserving the exact chronological sequence of events. Rebase modifies the history by moving your commits to a new starting point. Merge is safer for shared branches, while rebase is preferred for keeping a clean, linear history in individual feature branches.

Use 'merge' for integrating public or shared branches to preserve a transparent record of collaboration. Use 'rebase' for your private, local feature branches before merging them into the main line, ensuring that your final contribution is easy to follow and doesn't clutter the history with unnecessary merge commits.

Cherry-picking involves choosing a specific commit from one branch and applying it as a new commit on another branch. This is useful for porting a specific bug fix or small feature from a development branch into a production release without merging the entire branch and its unrelated changes.

Handling merge conflicts involves the manual process of deciding which code to keep when Git cannot automatically reconcile differences. It requires developer intervention to edit the conflicting files, choose the final implementation, and tell Git that the conflict is resolved by staging and committing the fixed files.

Strategies include 'theirs' (accepting all changes from the incoming branch), 'ours' (keeping all changes from the current branch), or 'manual' (the default, where the developer combines the best parts of both). Using tools like 'git mergetool' or IDE plugins can significantly simplify the manual resolution process.

Intermediate Git Topics9

The 'git stash' command temporarily shelves (or stashes) changes you've made to your working directory so you can work on something else, and then come back and re-apply them later. It is perfect for quickly switching branches without having to commit unfinished work.

You use git stash when you are in the middle of a task and a high-priority bug report comes in that requires you to switch branches immediately. Stashing allows you to clear your working directory for the bug fix without losing your current progress or creating 'WIP' commits.

'git stash apply' reintroduces the stashed changes to your working directory but keeps the copy in the stash list for future use. 'git stash pop' reintroduces the changes and immediately removes them from the stash list. Use 'apply' if you want to test the same stash on multiple branches.

The 'git stash list' command displays all the stashed changes currently stored in your repository. Each entry is shown with an index (e.g., stash@{0}), the branch name where it was created, and the commit message of the HEAD at that time, allowing you to identify which stash to re-apply.

A Detached HEAD state occurs when you check out a specific commit hash or a tag instead of a branch. In this state, the HEAD pointer refers directly to a commit rather than a symbolic branch name. Any new commits made here are not associated with any branch and can be lost if you switch away.

Checking out a specific commit hash moves your working directory to the state of the project at that exact moment in history. This is primarily used for inspecting older versions of the code or for debugging. It usually puts the repository into a 'Detached HEAD' state.

'git log --oneline' is a compact version of the commit history where each commit is displayed as a single line. It shows the first seven characters of the commit SHA and the commit message summary, making it much easier to scan through long histories quickly.

'git log --graph' draws a text-based representation of the commit history and branching structure. It uses lines and asterisks to show where branches diverged and merged, providing a visual understanding of how different lines of development have interacted over time.

Linear history is a straight line of commits without merge bubbles, often achieved through rebasing. Non-linear history includes merge commits that show exactly when and where branches diverged and rejoined. Linear is cleaner to read, while non-linear is a more accurate representation of actual collaborative events.

Undoing Changes13

'git reset' is a powerful command used to move the current branch HEAD to a specific commit. It can also be used to unstage files. Depending on the flags used, it can modify the index, the working directory, or both, effectively 'undoing' work by moving backwards in time.

'--soft' moves HEAD but leaves the index and working directory intact (changes remain staged). '--mixed' (default) moves HEAD and resets the index but leaves the working directory (changes become unstaged). '--hard' moves HEAD and resets both index and working directory, permanently deleting all uncommitted changes.

'git reset --soft' is safe; it only changes which commit the branch points to, keeping your actual code edits staged and ready for a new commit. 'git reset --hard' is destructive; it wipes out all your current edits and forces the working directory to exactly match the target commit.

'git revert' creates a new commit that introduces the exact opposite changes of a specified commit. Unlike 'reset', which removes commits from history, 'revert' preserves history and is safe for public shared branches because it only adds new information to the log to 'undo' a mistake.

'git reset' moves the branch pointer backward, effectively deleting commits from the current timeline (dangerous for shared branches). 'git revert' moves the branch forward by adding a new 'anti-commit' (safe for shared branches). Reset is for local cleanup; revert is for undoing public changes.

Use 'reset' when you made a mistake locally and haven't pushed your code yet, as it keeps the history clean. Use 'revert' when the mistake has already been pushed to a remote repository that other people are using, as it avoids rewriting and breaking their project history.

This legacy command is used to discard changes in the working directory for a specific file, reverting it to the state of the last commit. It is a quick way to 'undo' edits you haven't staged yet. Note that this command is being replaced by the more modern 'git restore'.

'git restore' is a modern command introduced to handle the file-undoing responsibilities previously held by 'checkout'. It can restore files in the working tree from the index, or restore the index from a specific commit, making the process of undoing specific file changes more intuitive.

To undo the last commit but keep your changes staged, use 'git reset --soft HEAD~1'. If you want to undo the commit and unstage the files but keep the edits, use 'git reset HEAD~1'. To completely delete the last commit and all associated edits, use 'git reset --hard HEAD~1'.

To undo all unstaged changes in your working directory, you can use 'git restore .' or the older 'git checkout -- .'. If you have new untracked files you also want to remove, you would need to use the 'git clean' command in addition to these.

To unstage a file (move it from the index back to the working directory) without losing the edits, use 'git restore --staged <file>' or the older 'git reset HEAD <file>'. This is useful when you've accidentally added a file to a commit that shouldn't be there.

'git clean' is used to remove untracked files from the working directory. While 'git checkout' or 'git restore' handle tracked files, 'git clean' removes the 'garbage' files like build logs or temporary artifacts that Git isn't currently monitoring.

'git clean -n' (dry run) shows you exactly which untracked files will be deleted without actually removing them, which is a safe first step. 'git clean -f' (force) actually executes the deletion. Because this operation is permanent and non-undoable, the dry run is highly recommended.

Viewing Diffs4

You can view the diff between two specific commits by using 'git diff <commit1-hash> <commit2-hash>'. This will show you every change in the code that occurred between those two snapshots, helping you track the evolution of a feature or locate when a bug was introduced.

To see how two branches differ, use 'git diff <branch1>..<branch2>'. This shows the changes in 'branch2' relative to 'branch1'. If you use three dots ('...'), it shows the changes in 'branch2' starting from the point where it last diverged from 'branch1'.

'git diff HEAD' compares your current working directory and your staging area against the last commit. This provides a 'total' view of all changes you have made (both staged and unstaged) since the project's last recorded snapshot.

To limit a diff or log to a specific file, simply append the file path to the command (e.g., 'git diff <hash> path/to/file.txt'). This filters out the 'noise' from other modified files, allowing you to focus on the history or changes of a single component.

Rewriting History7

The '--amend' flag allows you to modify the very last commit. It combines your current staged changes with the previous commit to create a new one. This is perfect for fixing a typo in a commit message or adding a forgotten file without creating a separate 'oops' commit.

You use '--amend' when you just committed and realized you forgot to stage a file, or if you made a spelling error in the commit message. It is only appropriate for local commits; you should avoid amending a commit that has already been pushed to a shared remote.

Interactive rebase allows you to edit, delete, reorder, or squash commits in your history. When you run it, Git opens a text editor listing the commits, and you can change the 'pick' command to 'squash' or 'edit' for each one, giving you total control over your local branch history.

This is a legacy tool for massive rewriting of Git history, such as removing a large file from every single commit or changing the email address for every commit in the repository. It has largely been replaced by faster and more robust tools like the 'git-filter-repo' python script.

This is a safer version of '--force'. It will only overwrite the remote branch if no one else has pushed new commits since your last fetch. It protects against accidentally overwriting a colleague's work while still allowing you to push a rewritten local history.

Rewriting history involves changing existing commits using tools like 'amend', 'rebase', or 'filter-branch'. Because Git commit hashes are based on content and metadata, changing a commit creates a brand new object, effectively creating an alternative timeline for the project.

If you rewrite history that others have already pulled, their local repositories will contain the 'old' timeline while the remote contains the 'new' one. This causes massive confusion and merge conflicts when they try to pull or push, often requiring manual reconstruction of the work.

GitHub Projects5

GitHub Projects are a customizable project management tool integrated directly into your repositories. They allow you to create spreadsheets, boards, and roadmaps to track issues and pull requests, helping teams plan and coordinate their development work in one central location.

Project planning involves using GitHub Issues, Milestones, and Projects to define the scope of work. By breaking down features into issues and grouping them into milestones with deadlines, teams can visualize progress and prioritize tasks effectively throughout the software development lifecycle.

Kanban boards in GitHub Projects are visual tools that use columns (like 'To Do', 'In Progress', 'Done') to track the status of work items. They help teams identify bottlenecks and ensure a smooth flow of tasks from initial idea to production release.

Roadmaps provide a high-level timeline view of project milestones and tasks. They allow stakeholders to see how long specific features will take and how they align with larger organizational goals, providing a birds-eye view of the project's long-term trajectory.

Automations allow the status of project items to update automatically based on actions in the repository. For example, an issue can move from 'To Do' to 'In Progress' automatically when a developer creates a linked Pull Request, reducing the manual administrative overhead for the team.

Working in a Team4

GitHub Organizations are shared accounts where businesses and open-source projects can manage multiple repositories and teams. They provide centralized billing, advanced security features, and a structured way to manage access permissions for large numbers of users across different projects.

A user account is personal and tied to an individual. An organization is a shared entity owned by multiple users. Organizations offer sophisticated team-management features, fine-grained access control, and centralized administration that personal accounts lack, making them essential for professional development teams.

Teams are sub-groups within an organization that allow you to manage access to repositories in bulk. For example, you can create a 'Frontend' team and give them write access to all UI-related repositories, rather than adding each developer individually to every project.

In an organization, you invite users as 'members'. You then assign them to 'teams' or add them directly to 'repositories' as 'collaborators' with specific roles (Read, Triage, Write, Maintain, Admin). This tiered system ensures that everyone has exactly the permissions they need for their job.

GitHub Actions15

GitHub Actions is an integrated automation platform that allows you to create custom software development lifecycle workflows directly in your repository. It is most commonly used for CI/CD, where code is automatically built, tested, and deployed every time a change is pushed.

CI/CD stands for Continuous Integration and Continuous Deployment. Continuous Integration is the practice of frequently merging code into a shared branch and running automated tests. Continuous Deployment is the automated process of releasing those tested changes to production without manual intervention.

A workflow is an automated process defined in a YAML file inside the '.github/workflows' directory. It consists of one or more 'jobs' that are triggered by specific events (like a push) and run on virtual machines or containers to perform tasks like linting or building.

YAML is a human-readable data serialization language used to configure GitHub Actions. Workflows use keys like 'name', 'on' (triggers), 'jobs', 'runs-on' (environment), and 'steps' (individual commands or actions) to define the sequence of automated events in a structured format.

Triggers define when a workflow should run. 'push' runs on every code upload. 'pull_request' runs when a PR is created or updated. 'schedule' uses cron syntax to run workflows at specific times (e.g., daily builds), providing flexible control over automation timing.

Scheduled workflows use the 'schedule' trigger with POSIX cron syntax to automate tasks that don't depend on code changes. Examples include weekly security scans, nightly dependency updates, or monthly cleanup tasks, allowing for maintenance without manual triggering.

A runner is a server that runs your GitHub Actions workflows. GitHub provides 'GitHub-hosted runners' (pre-configured Linux, Windows, or macOS VMs) for free or a fee. Alternatively, you can use 'self-hosted runners' if you need custom hardware or specialized local environment configurations.

Contexts are collections of variables that provide information about workflow runs, runner environments, jobs, and steps. Examples include the 'github' context (repo info, event data) and the 'env' context (environment variables), allowing for dynamic logic within your automation scripts.

Environment variables are non-sensitive configuration values. 'Secrets' are encrypted variables (like API keys or passwords) that you store in GitHub settings. They are never logged or exposed in the UI and are safely injected into workflows as needed for secure deployment.

Common use cases include running unit tests on every PR, automatically deploying a website to AWS/Vercel on push to main, linting code for style guide compliance, auto-assigning labels to issues, and sending Slack notifications for failed builds, covering the entire DevOps spectrum.

Client-side hooks reside in your local '.git/hooks' folder and trigger on local actions like 'pre-commit' or 'pre-push'. Server-side hooks run on the remote repository (like GitHub Enterprise) to enforce policies, such as rejecting pushes that don't follow naming conventions or contain large files.

Caching allows GitHub Actions to store and reuse files that don't change often, such as 'node_modules' or Maven dependencies. By using the 'actions/cache' action, you can significantly speed up your build times by avoiding redundant downloads from external package managers on every single run.

Artifacts are files produced during a workflow run, such as compiled binaries, test reports, or build logs. You use the 'actions/upload-artifact' action to save these files, allowing you to download them manually from the GitHub UI or share them between different jobs in the same workflow.

Workflow status indicates the current state of a GitHub Actions run. Common statuses include 'Queued', 'In progress', 'Success', 'Failure', or 'Cancelled'. These are displayed as icons next to commits and Pull Requests, providing immediate visual feedback on the health of your CI/CD pipeline.

The GitHub Marketplace is a central registry where developers share reusable Actions. Instead of writing your own scripts for common tasks like 'Deploy to AWS' or 'Post to Slack', you can simply reference a community-built action in your YAML file, promoting modularity and saving development time.

GitHub CLI & API8

GitHub CLI ('gh') is an open-source tool that brings GitHub features like issues, pull requests, and GitHub Actions to your local terminal. It allows developers to perform common platform tasks without switching to a web browser, increasing productivity for those comfortable with the command line.

Installation varies by OS, such as 'brew install gh' for macOS or 'sudo apt install gh' for Linux. Once installed, you run 'gh auth login' to authenticate with your GitHub account. This command guides you through a secure OAuth flow in your browser to grant the CLI necessary permissions.

Using GitHub CLI, you can run 'gh repo create' to start a new project, 'gh repo clone' to download an existing one, or 'gh repo view' to see documentation. It simplifies repository management by automating the remote linking process and handling visibility settings directly from your terminal session.

Issues can be managed using commands like 'gh issue list' to see open tasks, 'gh issue create' to report a bug, and 'gh issue status' to track assignments. You can also add labels and assignees using flags, making it possible to manage your project backlog entirely within the CLI.

GitHub CLI provides 'gh pr create' to initiate a review, 'gh pr checkout' to pull a colleague's code for testing, and 'gh pr merge' to integrate changes once approved. This enables a seamless 'terminal-only' workflow for the entire code review process, from initial submission to final integration.

The GitHub REST API allows external applications to interact with GitHub data using standard HTTP methods. It follows RESTful principles, where each resource (like a user, repo, or commit) has a specific URL. It is commonly used for building custom integrations, dashboard tools, or automation scripts.

The GraphQL API allows you to define exactly the data you need from GitHub in a single request. Unlike REST, which might require multiple calls to different endpoints, GraphQL uses a flexible schema that allows you to specify nested fields, reducing bandwidth and improving performance for complex data queries.

REST uses multiple fixed-structure endpoints and often suffers from 'over-fetching' (receiving too much data). GraphQL uses a single endpoint where the client dictates the response structure, ensuring you get exactly the fields you requested and nothing more, which is more efficient for modern, data-heavy applications.

Advanced Git Topics16

The Reflog is a local record of every time the tip of a branch (HEAD) was updated in your repository. Unlike 'git log', which shows commit history, 'reflog' shows all actions—including resets, checkouts, and amended commits—providing a safety net to recover 'lost' commits that are no longer reachable.

Bisect is a powerful debugging tool that uses a binary search algorithm to find which specific commit introduced a bug. You mark a 'good' commit and a 'bad' commit; Git then automatically checks out middle commits for you to test until it isolates the exact point of failure.

Git Worktree allows you to have multiple working directories attached to the same repository. This enables you to check out and work on different branches simultaneously in different folders without needing to stash your current work or perform multiple clones of the same project on your machine.

The '.gitattributes' file is a configuration file that allows you to define specific path-based settings. You can use it to force specific line-ending styles (LF vs CRLF), specify binary file types for diffing, or define how different files should be handled during merges and checkouts.

Git LFS is an extension that replaces large files (like high-res images, videos, or datasets) with tiny text pointers inside your repository. The actual large files are stored on a separate remote server. This prevents the repository size from ballooning and keeps standard Git operations fast and efficient.

You need Git LFS when your project involves non-text assets that exceed a few hundred megabytes, such as 3D models for games, large binary executables, or extensive database dumps. Standard Git handles these poorly because it tracks every historical version of these files, leading to massive repository downloads.

Git Hooks are scripts that Git executes automatically before or after events like 'commit', 'push', or 'receive'. They allow you to automate tasks like code linting, running unit tests, or verifying commit message formats, ensuring that every contribution follows the team's established quality and security standards.

'pre-commit' runs before a commit is created to check for syntax errors. 'post-commit' triggers after a commit to send notifications. 'pre-push' runs before code is uploaded to verify that all tests pass. These hooks help maintain local code health before changes are shared with the team.

Server-side hooks like 'pre-receive' and 'post-receive' run on the Git server (e.g., GitHub Enterprise). They are used to enforce global repository rules, such as preventing pushes to the main branch or ensuring that every commit contains a valid Jira ticket reference in the message.

A submodule is a Git repository embedded inside another repository as a subdirectory. It allows you to keep the history of a sub-project separate from the main project while still being able to 'pin' it to a specific commit, which is useful for sharing common libraries across multiple repositories.

Submodules maintain a separate link to another repository and require extra commands to initialize and update. Subtrees merge the code and history of another repository directly into your own. Subtrees are easier for others to use because they don't require special commands, but they result in a larger repository size.

To add a submodule, use 'git submodule add <url>'. This creates a '.gitmodules' file. To update it, you must run 'git submodule init' followed by 'git submodule update'. These steps ensure that the sub-project's files are downloaded at the exact version specified by the parent repository's current state.

A merge integrates all changes from another branch since it diverged. A cherry-pick selectively grabs only one specific commit and applies its changes to your current branch. Merge is for integrating whole features; cherry-pick is for porting individual fixes across different development versions.

To undo an accidental rebase, you first use 'git reflog' to find the commit hash where the branch was before the rebase started. You then use 'git reset --hard <old-hash>' to move your branch pointer back to its original stable state, effectively erasing the rebase attempt.

The 'git blame' command shows who modified each line of a file and in which commit the change occurred. It is a vital tool for understanding the history of a piece of code, identifying which team member to ask about a specific logic decision or bug fix.

A bare repository is a repository created without a working directory (using 'git init --bare'). It contains only the contents of the '.git' folder. Bare repositories are typically used on servers to act as central hubs for teams to push and pull code, as they don't allow local editing.

More GitHub Features21

GitHub Pages is a static site hosting service that takes HTML, CSS, and JavaScript files directly from a repository and publishes a website. It is commonly used for hosting project documentation, personal portfolios, and blogs, offering a free and easy way to go live with simple web content.

Navigate to repository 'Settings', then 'Pages'. Select a branch (usually 'main' or 'gh-pages') and a folder to serve from. Once saved, GitHub automatically builds and deploys your site to a URL like 'username.github.io/repo-name', allowing you to view your static site live within minutes.

Custom domains allow you to point your own purchased URL (like 'example.com') to your GitHub Pages site. You configure this by adding a 'CNAME' record to your DNS provider and entering the domain name in the GitHub Pages settings, providing a professional look for your project or personal site.

Static site generators like Jekyll take text files (usually Markdown) and template them into a full HTML website during the build process. GitHub Pages has native integration with Jekyll, allowing you to write blog posts in simple text and have GitHub automatically render them into a themed website.

GitHub Copilot is an AI-powered code assistant that provides real-time suggestions as you type. It can complete whole lines, functions, or even suggest complex logic based on the context of your comments and existing code, significantly speeding up the development process by handling repetitive boilerplate tasks.

Gists are a simple way to share code snippets, notes, or lists with others. Each Gist is a mini Git repository, meaning it can be versioned and cloned. They are perfect for sharing configuration files, bug reports, or small helper scripts that don't require a full project repository.

GitHub Packages is a software package hosting service that allows you to host your software packages (like npm, Docker, or Maven) privately or publicly. It is integrated with GitHub Actions and APIs, providing a secure and centralized location for managing your code and its distribution artifacts.

Codespaces is a cloud-based development environment that runs in your browser or through VS Code. It provides a pre-configured VM with all your dependencies, allowing you to start coding on any repository instantly without needing to set up a local development environment on your own machine.

GitHub Marketplace is a store where you can find and buy tools that integrate with your GitHub workflow. This includes GitHub Actions for CI/CD, security auditing tools, and project management apps, allowing you to extend the capabilities of your repository with vetted third-party services.

GitHub Education is a program that provides students, teachers, and schools with free access to professional-grade development tools. It includes the Student Developer Pack, which offers thousands of dollars' worth of free services from GitHub partners like Canva, Datadog, and DigitalOcean.

The Student Developer Pack is a suite of free tools and services available to students to help them learn by doing. It provides free access to GitHub Pro, cloud credits, domains, and premium versions of popular software, giving students the same tools used by professional developers around the world.

GitHub Classroom is a tool for teachers to manage coding assignments. It allows instructors to automatically create repositories for students, distribute starter code, and track progress using automated testing (autograding), making it much easier to teach and grade programming courses at scale.

The GitHub Campus Program is a partnership between GitHub and educational institutions. It provides the entire school with free GitHub Enterprise accounts, teacher training, and dedicated support, helping universities and colleges build a modern, collaborative technology curriculum for their students.

GitHub Security is a suite of features designed to keep your code safe. This includes Dependabot (which alerts you to vulnerable dependencies), Secret Scanning (which detects leaked API keys), and Code Scanning (which uses static analysis to find potential security bugs in your source code).

GitHub Sponsors allows the community to financially support the developers and organizations who maintain the open-source projects they depend on. It provides a way for individuals and companies to contribute recurring payments to open-source contributors directly through their GitHub profiles.

Dependabot is an automated tool that scans your project's dependencies for known security vulnerabilities. When a flaw is found, it automatically creates a Pull Request to update the dependency to a safe version, helping you keep your application secure with minimal manual effort.

Secret scanning is a security feature that searches your repository for sensitive credentials like AWS keys, Stripe tokens, or private certificates that might have been accidentally committed. GitHub notifies the service provider immediately to revoke the key, preventing attackers from exploiting the leak.

CodeQL is the analysis engine behind GitHub's code scanning feature. It treats your code like data that can be queried, allowing it to find complex security vulnerabilities and logic errors that standard linters might miss. It supports many languages including Java, Python, and C++.

Environment secrets are encrypted variables that are specific to a deployment environment, such as 'production' or 'staging'. This allows you to use the same workflow file while securely rotating different API keys or database credentials based on where the code is being deployed.

The GitHub Advisory Database is a collection of security advisories for vulnerabilities in open-source projects. It powers Dependabot and provides detailed information about each flaw, including which versions are affected and the severity level, helping the community stay informed about security risks.

Organizations offer enhanced security features like Single Sign-On (SSO), IP allow-lists, and the ability to require two-factor authentication (2FA) for all members. These centralized controls are vital for enterprise security compliance, ensuring that company data is protected even if individual user accounts are compromised.

Related