Git is a distributed version control system designed to track changes in source code and facilitate collaboration among developers. It enables teams to work concurrently on projects without overwriting each other's work, providing a complete and auditable history of all modifications.
Repository Initialization and User Configuration
To begin managing a project with Git, navigate to your project directory and initialize a new repository:
git init
This command creates a hidden .git subdirectory, which stores all the necessary version control information. Each project typically corresponds to a single Git repository.
It is crucial to configure your user identity, as this information is embedded into every commit you make:
git config --global user.name "Your Full Name"
git config --global user.email "your.email@example.com"
To inspect your current Git configurations, including user identity:
git config --list
Managing File States: Staging and Committing
Git categorizes changes into three states: the working directory (where you make modifications), the staging area (an intermediate holding area for changes you want to include in the next commit), and the local repository (where committed changes are permanently stored).
Inspecting Repository Status:
The git status command provides a summary of your repository's current state, highlighting untracked files, modified files, and staged changes:
git status
Staging Changes:
Before committing, changes must be moved from the working directory to the staging area. This allows you to selectively include modifications in a commit.
git add new_feature.js # Stage a specific file
git add assets/ # Stage all files within a directory
git add . # Stage all modified and new files in the current directory and its subdirectories
Committing Changes:
Once changes are staged, they can be recorded as a new commit in the local repository. Each commit should be accompanied by a concise, descriptive message.
git commit -m "Implement user profile update functionality"
Ignoring Unwanted Files:
To prevent certain files (e.g., build outputs, temporary files, configuration files) from being tracked by Git, create a .gitignore file in the repository's root directory. List patterns for files or directories to be ignored.
# Example .gitignore content
*.tmp
/build/
.env
Once created, stage and commit the .gitignore file itself so that its rules are shared with all collaborators.
Viewing History and Undoing Modifications
Reviewing Commit History:
To explore the project's commit history, use git log.
git log --oneline --graph --all --decorate
This command displays a compact, graphical representation of commits across all branches, including commit IDs and messages.
Comparing Changes:
Determine the differences between various states of your files:
git diff # Changes in the working directory not yet staged
git diff --staged # Changes in the staging area
git diff commit_hash_A commit_hash_B # Differences between two specific commits
Unstaging Files:
To remove a file from the staging area without reverting your local modifications:
git reset HEAD server_config.xml
Reverting to Previous Versions:
The git reset command allows you to move the branch pointer to an earlier commit.
-
--soft: Moves the branch pointer, but keeps all changes in your staging area and working directory, allowing you to recommit them.
git reset --soft HEAD~1 # Move back one commit, retain changes staged ```
-
--hard: Moves the branch pointer and discards all changes in the staging area and working directory, making them identical to the target commit. Use this with extreme caution as changes will be permanently lost.
git reset --hard HEAD~1 # Move back one commit, discard all subsequent changes git reset --hard <target_commit_id> # Revert to a specific commit, erasing all changes made after it ```
Recovering Lost Commits:
Even after a hard reset, git reflog can often help recover lost work. It records every change to HEAD (the current commit your branch points to).
git reflog
This log shows a history of your branch's movements. You can then use git reset --hard <reflog_entry_id> to restore a previous state.
Branch Management
Git branches allow developers to diverge from the main development line to work on new features or fixes independently without affecting the stable codebase.
Listing Branches:
git branch # Show local branches
git branch -a # Show both local and remote-tracking branches
git branch -vv # Show local branches, their corresponding remote branches, and the last commit
HEAD indicates your currently active branch.
Creating New Branches:
git branch experimental_feature
Switching Between Branches:
git checkout experimental_feature # Switch to an existing branch
git switch experimental_feature # Modern alternative for switching branches
Creating and Switching (Combined):
git checkout -b hotfix_release # Create 'hotfix_release' and switch to it
git switch -c hotfix_release # Modern alternative using 'git switch'
Deleting Branches:
git branch -d completed_feature # Delete a branch only if it has been merged
git branch -D work_in_progress # Force delete a branch, even if unmerged (use carefully)
Merging Branches:
Integrating changes from one branch into another:
-
git merge: Combines the histories of two branches, typically by creating a new merge commit. This preserves the individual commit histories of both branches, showing exactly where the merge occurred.# Assuming you are on the 'main' branch
git merge feature_branch # Integrate 'feature_branch' into 'main' ```
-
git rebase: Reapplies a series of commits from one branch onto another. This results in a linear history, making it appear as if the feature branch was developed directly on top of the target branch. Rebase rewrites commit history and should be used cautiously on shared branches.# Assuming you are on 'feature_branch'
git rebase main # Reapply 'feature_branch' commits on top of 'main' # After rebase, switch to 'main' and fast-forward merge git switch main git merge feature_branch ```
rebase is often preferred for local feature branches to maintain a clean, linear project history before integrating into a main branch, whereas merge is generally safer for shared branches.
Resolving Merge Conflicts:
Conflicts arise when Git cannot automatically combine changes (e.g., the same line was modified differently in two branches). When a conflict occurs:
- Git will notify you during the merge or rebase operation.
- Use
git statusto identify the files containing conflicts. - Manually edit the conflicting files. Git inserts conflict markers (
<<<<<<<,=======,>>>>>>>) to highlight the conflicting sections. Choose the desired changes and remove the markers. - After resolving, stage the modified file:
git add conflicted_file.txt. - Complete the operation:
git commitfor a merge orgit rebase --continuefor a rebase.
Interacting with Remote Repositories
Remote repositories, hosted on platforms like GitHub or GitLab, facilitate collaboration and serve as central backups.
SSH Key Setup:
For secure communication with remote repositories, ganerate an SSH key pair on your local machine and add the public key to your Git hosting service's settings.
ssh-keygen -t rsa -b 4096 # Generate an RSA key with 4096 bits
cat ~/.ssh/id_rsa.pub # Display the public key to copy
Follow the prompts, then copy the content of id_rsa.pub to your remote provider's SSH key management section.
Cloning a Repository:
To obtain a copy of an existing remote repository onto your local machine:
git clone git@github.com:youruser/my-project.git
This command creates a new directory, my-project, containing the entire project history and all files.
Linking a Local Repository to a Remote:
If you started with a local repository and want to connect it to a new remote:
git remote add origin git@github.com:youruser/new-repo.git
origin is the conventional name for the primary remote repository. Verify the connection:
git remote -v
Fetching Updates from Remote:
Download changes from the remote repository without integrating them into your current local branch:
git fetch origin
Pulling Changes from Remote:
Fetch changes from the remote and automatically merge them into your current local branch:
git pull origin main # Pull from the 'main' branch on the 'origin' remote
This operation is equivalent to git fetch followed by git merge. If conflicts occur, resolve them as described in the branching section.
Pushing Changes to Remote:
Upload you're local commits to the remote repository:
git push origin main # Push local 'main' branch commits to remote 'main'
-
Setting Upstream Tracking: The first time you push a new local branch, it's good practice to set up upstream tracking:
git push -u origin new-feature-branch # Links local 'new-feature-branch' to 'origin/new-feature-branch' ```
This allows subsequent `git pull` and `git push` commands for that branch to be executed without explicitly specifying the remote and branch names. You can view tracking status using `git branch -vv`.
-
Force Pushing:
git push --force origin main # Overwrites the remote branch with your local history ```
Use `git push --force` with extreme caution, as it can erase work from collaborators. It is generally only safe for branches that are strictly personal or immediately after a `git rebase` on a private branch.
Handling Remote Conflicts during Push:
If your local branch is behind the remote (i.e., new commits exist on the remote that you don't have locally), Git will reject your push. You must first git pull to integrate the remote changes, resolve any conflicts that arise, and then attempt to git push again.
A Common Development Workflow Example
Here's a typical sequence of Git commands during feature development:
-
Start by ensuring your local
mainbranch is up-to-date:
git switch main git pull origin main ```
-
Create a new branch for your specific feature or task:
git switch -c create_analytics_report ```
-
Implement your changes.
-
Regularly stage and commit your work:
git status git add . # Stage all relevant modifications git commit -m "feat: Develop initial framework for analytics report" ```
-
Periodically integrate updates from the
mainbranch into your feature branch to avoid large conflicts later (often using rebase for a clean history):
git fetch origin main git rebase origin/main # Reapply your feature commits on top of the latest 'main' # Resolve any conflicts if they occur during the rebase ```
-
Push your feature branch to the remote for backup and to enable code reviews:
git push -u origin create_analytics_report ```
-
Once the feature is complete and approved, merge it into the
mainbranch:
git switch main git pull origin main # Ensure 'main' is fully up-to-date before merging git merge create_analytics_report # Integrate the feature branch # Resolve any conflicts that arise during the merge git commit -m "Merge branch 'create_analytics_report' into main" # A merge commit message is often pre-filled git push origin main # Push the updated 'main' branch to remote ```
-
Finally, delete the completed feature branch locally and remotely (after successful merge and push to main):
git branch -d create_analytics_report git push origin --delete create_analytics_report ```