← Back to Articles         
Gitignore, Main Branch Protection, and Team Git Best Practices

Gitignore, Main Branch Protection, and Team Git Best Practices

Published on 03 Oct 2026     10 min read Git & GitHub
Git Security

Gitignore, Main Branch Protection, and Team Git Best Practices

These three topics are closely related to keeping a shared Git repository secure, stable, and easy for a development team to maintain.


1. Does adding a file to .gitignore remove it from Git history?

What is it?

No. Adding a file to .gitignore does not remove it from Git history.

.gitignore tells Git which untracked files or folders should normally not be added to version control.

For example:

.env
appsettings.Production.json
bin/
obj/

If .env has never been committed, Git will normally ignore it.

But if the file has already been committed, simply adding it to .gitignore does not:

  • Stop Git from tracking the already tracked file.
  • Delete it from previous commits.
  • Remove sensitive information from Git history.

Simple Example

Suppose:

Commit A
   |
   |-- appsettings.Production.json
   |      Password=Secret123
   |
Commit B
   |
   |-- Added file to .gitignore

The old version still exists:

Commit A → Secret still exists
Commit B → File added to .gitignore

Someone with access to the repository history could potentially inspect the earlier commit.


If the file is already tracked

Suppose you have:

appsettings.Production.json

but Git is already tracking the file.

You can remove it from the Git index while keeping your local copy:

git rm --cached appsettings.Production.json

Then commit:

git add .gitignore
git commit -m "Stop tracking production settings"
git push

Now:

Local file
    ↓
Still exists

Git current version
    ↓
No longer tracks it

However:

Previous Git commits
        ↓
May still contain the file

If it contained a real secret, rotate/revoke the secret and consider history cleanup as discussed previously.


Key Points

  • .gitignore primarily affects untracked files.
  • It doesn't automatically untrack files already committed.
  • Use git rm --cached <file> to stop tracking an already tracked file while retaining the local file.
  • It doesn't remove the file from previous commits.
  • If credentials were exposed, removing the file is not sufficient; rotate/revoke the credentials.

Interview Answer

No. Adding a file to .gitignore does not remove it from Git history. .gitignore mainly prevents untracked files from being added. If a file is already tracked, I can use git rm --cached to stop tracking it, but previous commits can still contain the file. If it contained a secret, I would rotate the secret and clean the repository history if required.


2. How do you protect the main branch?

What is it?

Protecting main means configuring repository policies so developers cannot make uncontrolled changes to the production-critical branch.

Instead of:

Developer
    |
    | git push
    v
   main

the team normally follows:

Developer
    ↓
Feature Branch
    ↓
Pull Request
    ↓
Build / Tests
    ↓
Code Review
    ↓
Approval
    ↓
main

This is primarily a Git hosting/platform feature, not a feature of the core Git command itself.

For example:

  • GitHub → branch protection/rulesets
  • Azure DevOps → branch policies

Simple Example

Developer creates a feature branch:

git switch main
git pull origin main

git switch -c feature/login

Makes changes:

git add .
git commit -m "Add login functionality"

Pushes:

git push -u origin feature/login

Instead of directly pushing:

git push origin main

the developer creates a Pull Request:

feature/login
      ↓
Pull Request
      ↓
Required Reviewers
      ↓
Build Validation
      ↓
Tests
      ↓
Approval
      ↓
main

Important main Branch Protections

1. Require Pull Requests

Prevent normal development changes from going directly into main.

feature/login
     ↓
     PR
     ↓
    main

This gives the team an opportunity to review the change.


2. Require Reviewers

For example:

Developer creates PR
        ↓
Reviewer reviews code
        ↓
Approval
        ↓
Merge allowed

Teams can require one or more approvals depending on their process.


3. Require Successful CI Checks

Before merging:

Pull Request
     ↓
Build
     ↓
Unit Tests
     ↓
Code Quality / Security Checks
     ↓
Success
     ↓
Merge

If the build fails:

Build failed
     ↓
Merge blocked

This prevents obviously broken code from entering main.


4. Restrict Direct Pushes

Developers should generally not bypass the Pull Request process:

Developer
   X
   |
   v
 main

Instead:

Developer
    ↓
Branch
    ↓
PR
    ↓
main

5. Restrict Force Push

This command can rewrite shared history:

git push --force

On an important shared branch such as main, force pushing should normally be disabled or tightly restricted.


6. Control Branch Deletion

Accidental deletion of important branches should also be restricted.


GitHub vs Azure DevOps

The terminology differs slightly.

GitHub Azure DevOps
Branch protection/rulesets Branch policies
Pull Request Pull Request
Required reviews Minimum number of reviewers / reviewer policies
Required status checks Build validation/status policies
Restrict pushes Branch permissions/policies

The exact options vary by platform and organization configuration.


Practical Team Workflow

A common protected-branch workflow is:

main
 |
 +------ feature/login
             |
          Development
             |
           Commit
             |
            Push
             |
             v
        Pull Request
             |
     +-------+-------+
     |               |
Code Review       CI Pipeline
     |               |
Approval        Build + Tests
     |               |
     +-------+-------+
             |
             v
           Merge
             |
             v
            main

Key Points

  • Protect main from uncontrolled direct changes.
  • Require Pull Requests.
  • Require appropriate reviewer approval.
  • Require successful build/test checks.
  • Restrict force pushes.
  • Use branch permissions appropriately.
  • GitHub provides branch protection/rulesets.
  • Azure DevOps provides branch policies and permissions.

Interview Answer

I protect the main branch by preventing normal direct pushes and requiring developers to work through feature branches and Pull Requests. I configure required reviewers, successful CI/build validation and other status checks before merging. I also restrict force pushes and branch deletion. In GitHub this is handled through branch protection or rulesets, while in Azure DevOps we use branch policies and permissions.


3. Important Git Best Practices for Development Teams

What is it?

Git best practices are conventions that help a team maintain:

  • Clean history
  • Stable branches
  • Easier collaboration
  • Better code review
  • Fewer merge conflicts
  • Secure repositories
  • Reliable releases

1. Use a Defined Branching Strategy

The whole team should follow the same strategy.

Examples:

GitFlow
GitHub Flow
Trunk-Based Development

Avoid developers independently inventing different branching processes.


2. Protect main

Don't allow normal development to happen directly on main.

Prefer:

main
 |
 +---- feature/login
 |
 +---- feature/payment

Then use Pull Requests.


3. Keep Branches Focused

A branch should ideally address one feature, bug, or task.

Good:

feature/login
bugfix/cart-total
hotfix/payment-error

Avoid one branch containing many unrelated changes.

This makes PRs easier to review and reduces merge complexity.


4. Keep Branches Short-Lived

Long-lived branches tend to diverge from the shared branch:

main    A---B---C---D---E---F
         \
feature   X-------------Y

The longer they diverge, the greater the potential integration difficulty.

Prefer frequent synchronization and smaller changes.


5. Pull/Fetch Recent Changes Regularly

Before starting or integrating work, synchronize appropriately with the remote repository.

For example:

git switch main
git pull origin main

Then create the new branch:

git switch -c feature/login

While working, keep aware of changes made by other developers.


6. Make Small, Logical Commits

Avoid one huge commit containing unrelated work.

Bad:

"Changed many things"

Better:

Add login validation
Add JWT authentication
Handle invalid credentials
Add authentication unit tests

Each commit should represent a meaningful logical change.


7. Write Meaningful Commit Messages

Avoid:

git commit -m "changes"
git commit -m "fix"
git commit -m "test"
git commit -m "final"

Prefer:

git commit -m "Add validation for login request"

or:

git commit -m "Fix null reference in order service"

A reviewer should be able to understand the purpose of the commit.


8. Use Pull Requests and Code Reviews

Instead of:

Developer → main

prefer:

Developer
    ↓
Feature Branch
    ↓
Pull Request
    ↓
Review
    ↓
CI
    ↓
main

Pull Requests provide a controlled place for discussion, review, and automated validation.


9. Resolve Conflicts Carefully

Don't blindly accept:

Accept Current

or:

Accept Incoming

Understand both changes before resolving the conflict.

After resolution:

git add <resolved-file>

Then complete the merge/rebase operation as appropriate.

Most importantly, test the resulting code.

A conflict-free merge does not automatically mean the application behaves correctly.


10. Never Commit Secrets

Never intentionally commit:

Passwords
API keys
PATs
Private keys
JWT signing secrets
Credential-bearing connection strings

Use:

Environment variables
.NET User Secrets
Azure Key Vault
CI/CD secret variables

11. Use .gitignore Properly

Don't commit generated/local files unnecessarily.

For a .NET application, common examples include:

bin/
obj/
.vs/

The repository should contain source and necessary configuration—not every file generated on a developer's machine.


12. Be Careful With History-Rewriting Commands

Commands such as:

git reset --hard
git rebase
git push --force

can cause lost work or collaboration problems when used incorrectly.

Particularly avoid rewriting commits that other developers are already depending on unless the team has explicitly coordinated it.

For force pushes where rewriting is intentional, --force-with-lease is generally safer than plain --force because it provides additional protection against overwriting unexpected remote changes.


13. Delete Merged Feature Branches

After:

feature/login
      ↓
     PR
      ↓
    main

the feature branch is usually no longer required.

Local:

git branch -d feature/login

Remote:

git push origin --delete feature/login

This keeps the repository easier to navigate.


14. Tag Important Releases

For example:

git tag v2.0.0
git push origin v2.0.0

Then:

main
 |
 +-- v1.0.0
 |
 +-- v1.1.0
 |
 +-- v2.0.0

Tags make it easier to identify the exact commit associated with a release.


15. Use CI/CD Validation

A strong workflow combines Git with automated validation:

Developer
    ↓
Commit
    ↓
Push
    ↓
Pull Request
    ↓
CI Pipeline
    |
    +-- Restore dependencies
    +-- Build
    +-- Unit tests
    +-- Static/security checks
    |
    ↓
Review / Approval
    ↓
Merge

This prevents many problems from reaching the protected branch.


Team Git Workflow Example

A practical workflow might be:

1. Update main
       ↓
2. Create feature branch
       ↓
3. Make small changes
       ↓
4. Commit with meaningful messages
       ↓
5. Synchronize with latest team changes as needed
       ↓
6. Push feature branch
       ↓
7. Create Pull Request
       ↓
8. CI build + tests
       ↓
9. Code review
       ↓
10. Resolve comments/conflicts
       ↓
11. Merge into protected main
       ↓
12. Delete feature branch
       ↓
13. Deploy through CI/CD

Key Points

For quick interview revision, remember these:

  1. Follow a clear branching strategy.
  2. Protect main.
  3. Use short-lived, focused branches.
  4. Use Pull Requests and code reviews.
  5. Keep commits small and logical.
  6. Write meaningful commit messages.
  7. Synchronize regularly to reduce integration problems.
  8. Never commit secrets.
  9. Configure .gitignore correctly.
  10. Test carefully after resolving conflicts.
  11. Be cautious with reset --hard, rebase, and force push.
  12. Delete merged feature branches when no longer needed.
  13. Tag releases when appropriate.
  14. Use automated CI/build/test checks.
  15. Keep the shared branch stable and deployable according to the team's release model.

Interview Answer

Adding a file to .gitignore does not remove it from Git history. If it is already tracked, I can use git rm --cached to stop tracking it, but earlier commits still contain the historical version.

For the main branch, I normally protect it by requiring Pull Requests, code reviews, successful CI/build checks, and appropriate approvals while restricting direct pushes, force pushes, and deletion.

In a development team, I follow a defined branching strategy, use short-lived focused branches, make small logical commits with meaningful messages, regularly synchronize changes, use Pull Requests and code reviews, never commit secrets, and use CI/CD validation. I am also careful with history-rewriting commands such as rebase, reset, and force push, especially on shared branches.

← Back to Articles