DEV Community

Cover image for Complete Guide to Git for Multi-Platform Development: From Mac to Windows Without Conflicts
Antonio
Antonio

Posted on

Complete Guide to Git for Multi-Platform Development: From Mac to Windows Without Conflicts

Introduction: The Problem Every Cross-Platform Developer Knows

Raise your hand if you've never found yourself in this situation: you work on a project on your Mac at the office, come home, open your Windows laptop, do a git pull and... boom! Conflicts everywhere. Files that appear modified even though you've never touched them. Configurations that don't work. Paths that don't exist. Line endings gone crazy.

If you recognized yourself in this description, this article is for you. After months of work on my Smart Conda Terminal project – a VSCode extension that needs to work perfectly on both macOS and Windows – I learned (often the hard way) how to manage a truly multi-platform Git workflow.

In this article, I'll share everything I've learned: from initial configuration to best practices, from tricks to avoid conflicts to solutions for the most common problems. Not a theoretical guide, but a practical manual born from field experience.

Why is Multi-Platform Development So Complicated?

Before diving in, let's understand why Git makes our lives difficult when we work on different operating systems:

1. Paths are Different

// macOS
"python.defaultInterpreterPath": "/Users/tony/miniconda3/envs/sct-dev/bin/python"

// Windows
"python.defaultInterpreterPath": "C:\\Users\\Tony\\miniconda3\\envs\\sct-dev\\python.exe"
Enter fullscreen mode Exit fullscreen mode

2. Line Endings are Different

  • Unix/Mac use LF (Line Feed: \n)
  • Windows uses CRLF (Carriage Return + Line Feed: \r\n)

Result? Git sees every single file as modified, even if you haven't changed a comma.

3. Shell Scripts Don't Work on Windows

Bash scripts that run perfectly on Mac? On Windows you need PowerShell or WSL.

4. VSCode Configurations are Platform-Specific

Terminal profiles, conda paths, interpreter settings... all different.

The Strategy: One Repository, Two Platforms, Zero Conflicts

The solution I developed is based on three principles:

  1. Shared configurations for everything that is platform-agnostic
  2. Local files ignored by Git for platform-specific paths and settings
  3. Intelligent automation that detects the platform

Let's see how to implement it step by step.

Initial Setup: Preparing the Ground

1. Cross-Platform Git Configuration

First of all, let's configure Git correctly on both platforms:

# Name and email (same on both)
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

# Line endings - CRUCIAL to avoid conflicts
# On Mac/Linux:
git config --global core.autocrlf input

# On Windows:
git config --global core.autocrlf true

# Editor and other useful settings
git config --global core.editor "code --wait"
git config --global push.default simple
git config --global color.ui auto
Enter fullscreen mode Exit fullscreen mode

2. The .gitattributes File: The Line Endings Savior

Create this file in the project root and commit it:

text

# .gitattributes
* text=auto eol=lf
*.sh text eol=lf
*.js text eol=lf
*.json text eol=lf
*.md text eol=lf
*.py text eol=lf

# Binary files
*.png binary
*.jpg binary
*.ico binary
Enter fullscreen mode Exit fullscreen mode

This file tells Git: "Whatever the operating system, always use LF internally, and let the local system decide how to save files."

3. The Intelligent .gitignore

A well-crafted .gitignore is essential. Here's mine:

# Dependencies
node_modules/
.node_modules/
npm-debug.log*
package-lock.json

# Build outputs
out/
dist/
*.vsix

# OS files - Here's the trick!
.DS_Store          # macOS
Thumbs.db          # Windows
*.swp
*.swo

# IDE - Keep only shared configs
.vscode/settings.json.local
.vscode/*.local.json

# IMPORTANT: DO NOT ignore these
!.vscode/settings.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/tasks.json

# Logs and cache
*.log
.npm
.cache/

# Environment
.env
.env.local

# Conda specific
.smart-conda-terminal/
conda-meta/
Enter fullscreen mode Exit fullscreen mode

The key is in the IDE section: we keep shared configurations (with !) but ignore local ones.

Project Structure: What to Commit and What Not

project/
β”œβ”€β”€ .vscode/
β”‚   β”œβ”€β”€ settings.json       ← COMMITTED (general settings)
β”‚   β”œβ”€β”€ launch.json         ← COMMITTED (debug config)
β”‚   β”œβ”€β”€ extensions.json     ← COMMITTED (recommended extensions)
β”‚   └── tasks.json          ← COMMITTED (common tasks)
β”œβ”€β”€ .gitattributes          ← COMMITTED
β”œβ”€β”€ .gitignore              ← COMMITTED
β”œβ”€β”€ package.json
β”œβ”€β”€ src/
└── project.code-workspace  ← OPTIONAL, see below
Enter fullscreen mode Exit fullscreen mode

The Secret: Two-Level Configurations

Level 1: Shared Settings (.vscode/settings.json - committed)

{
    "editor.formatOnSave": true,
    "editor.insertSpaces": true,
    "editor.tabSize": 2,
    "files.trimTrailingWhitespace": true,
    "files.exclude": {
        "out": false,
        "dist": true,
        "node_modules": true,
        "**/.DS_Store": true,
        "**/Thumbs.db": true
    }
}
Enter fullscreen mode Exit fullscreen mode

Level 2: Workspace with Environment Variables (optional)

Instead of hard-coded paths, use variables:

{
    "folders": [{"path": "."}],
    "settings": {
        "python.defaultInterpreterPath": "${env:CONDA_PREFIX}/bin/python",
        "python.terminal.activateEnvironment": true
    }
}
Enter fullscreen mode Exit fullscreen mode

On Windows, ${env:CONDA_PREFIX} will automatically resolve to the correct path.

Daily Workflow: The Four Commandments

1. Before Starting: PULL

cd your-project
git pull origin main
Enter fullscreen mode Exit fullscreen mode

Simple but fundamental. Avoids 90% of conflicts.

2. During Work: Small and Frequent Commits

❌ Bad:

# After 3 days...
git commit -m "various changes"
Enter fullscreen mode Exit fullscreen mode

βœ… Good:

git commit -m "feat: Add Windows terminal profile support"
git commit -m "fix: Python path resolution on Windows"
git commit -m "docs: Update cross-platform setup guide"
Enter fullscreen mode Exit fullscreen mode

3. Before Finishing: Verify

# What did I modify?
git status

# See the differences in detail
git diff

# Everything ok? Add
git add .

# Commit with clear message
git commit -m "feat: Implement auto-platform detection"
Enter fullscreen mode Exit fullscreen mode

4. When You Finish: PUSH

git push origin main
Enter fullscreen mode Exit fullscreen mode

Conflict Management: Real Situations and How to Solve Them

Scenario 1: "Your local changes would be overwritten"

The problem: You have uncommitted local changes and do git pull.

The solution (stash method):

# 1. Set aside your changes
git stash

# 2. Download updates
git pull origin main

# 3. Reapply your changes
git stash pop

# 4. If there are conflicts, resolve them manually
Enter fullscreen mode Exit fullscreen mode

Scenario 2: Merge Conflicts

When Git can't merge automatically, you'll see this:

<<<<<<< HEAD
const version = "1.0.0";  // Your version
=======
const version = "2.0.0";  // Remote version
>>>>>>> origin/main
Enter fullscreen mode Exit fullscreen mode

How I solve it:

  1. Open the file in VSCode
  2. Decide which version to keep (or merge both)
  3. Remove the markers <<<<<<<, =======, >>>>>>>
  4. Save and commit:
git add conflicted-file.js
git commit -m "resolve: Merge conflict in version number"
git push origin main
Enter fullscreen mode Exit fullscreen mode

Scenario 3: Crazy Line Endings

Symptoms: Git shows modified files even though you haven't touched them.

Diagnosis:

git diff file.js
# If you only see "^M" or invisible differences, it's line endings
Enter fullscreen mode Exit fullscreen mode

Cure:

# 1. Add .gitattributes (if not already there)
# 2. Normalize all files
git add --renormalize .
git commit -m "fix: Normalize line endings"
git push origin main
Enter fullscreen mode Exit fullscreen mode

Automation Scripts: Platform Detection

For scripts that need to work everywhere:

#!/bin/bash

# Detect platform
detect_platform() {
    case "$(uname -s)" in
        Darwin*)    echo "mac" ;;
        Linux*)     echo "linux" ;;
        MINGW*|MSYS*|CYGWIN*)  echo "windows" ;;
        *)          echo "unknown" ;;
    esac
}

# Get correct conda path
get_conda_path() {
    PLATFORM=$(detect_platform)

    case $PLATFORM in
        mac|linux)
            echo "$HOME/miniconda3"
            ;;
        windows)
            echo "$USERPROFILE/miniconda3"
            ;;
    esac
}

# Use the function
CONDA_PATH=$(get_conda_path)
echo "Conda path: $CONDA_PATH"
Enter fullscreen mode Exit fullscreen mode

Best Practices: The Golden Rules

1. Meaningful Commit Messages

I use the Conventional Commits format:

feat: Add new feature
fix: Fix bug
docs: Update documentation
style: Code formatting
refactor: Code restructuring
test: Add tests
chore: General maintenance
Enter fullscreen mode Exit fullscreen mode

2. Pull Requests for Important Changes

# Create a branch for the feature
git checkout -b feature/new-feature

# Work on the branch
git add .
git commit -m "feat: Implement new feature"

# Push the branch
git push origin feature/new-feature

# On GitHub: create Pull Request
# After review: merge to main
Enter fullscreen mode Exit fullscreen mode

3. Don't Commit Generated Files

Never ever commit:

  • node_modules/
  • Build outputs (dist/, out/)
  • Log files
  • Local configurations
  • Credentials

4. Test on Both Platforms Before Pushing

Personal checklist:

  • Does the code work on Mac?
  • Does the code work on Windows?
  • Do tests pass on both?
  • Is the documentation updated?
  • Is the commit message clear?

Troubleshooting: The Most Common Problems

"Permission denied (publickey)"

Problem: Git can't connect to GitHub.

Solution:

# Generate SSH key
ssh-keygen -t ed25519 -C "your.email@example.com"

# Copy the key
# Mac:
pbcopy < ~/.ssh/id_ed25519.pub
# Windows:
cat ~/.ssh/id_ed25519.pub | clip

# Add it on GitHub: Settings β†’ SSH Keys

# Test
ssh -T git@github.com
Enter fullscreen mode Exit fullscreen mode

"Repository too large"

Problem: The repository has become huge.

Solution:

# Remove untracked files
git clean -fd

# Compress
git gc --aggressive --prune=now
Enter fullscreen mode Exit fullscreen mode

"Detached HEAD state"

Problem: You checked out a specific commit.

Solution:

# Return to main branch
git checkout main
Enter fullscreen mode Exit fullscreen mode

Tools That Simplify Life

1. GitHub Desktop

Simple and intuitive GUI, perfect for beginners.

2. GitKraken

Advanced graphical visualization of branches and history.

3. VSCode Built-in Git

My favorite. Perfect integration with the editor.

4. Git Aliases

Shortcuts for frequent commands:

git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.lg "log --oneline --graph --all"
Enter fullscreen mode Exit fullscreen mode

Now I can do git st instead of git status.

Conclusion: The Perfect Workflow Exists

After months of experimentation, my daily workflow has been reduced to this:

Morning (start work):

git pull origin main
Enter fullscreen mode Exit fullscreen mode

Evening (end work):

git status
git add .
git commit -m "feat: Clear description"
git push origin main
Enter fullscreen mode Exit fullscreen mode

In case of problems:

git stash
git pull origin main
git stash pop
Enter fullscreen mode Exit fullscreen mode

Simple, right? The key is in the correct initial configuration. Once everything is set up properly, Git becomes an ally, not an enemy.

Final Checklist for Your Project

  • .gitattributes configured for line endings
  • Complete and tested .gitignore
  • Multi-platform Git configuration (core.autocrlf)
  • Two-level VSCode settings (shared + local)
  • Scripts with automatic platform detection
  • SSH keys configured on both platforms
  • Workflow documented for the team
  • Tests on both operating systems

Useful Resources


Do you have questions or suggestions? Write to me in the comments! This workflow is constantly evolving and I'm always curious to discover how other developers handle the same problem.

If the article was useful to you, leave a clap πŸ‘ and share it with other developers who work on multiple platforms!


Final note: All the code and configurations shown in this article are taken from my real project Smart Conda Terminal, a VSCode extension for intelligent management of Conda environments. The repository is public and can serve as a practical example of what's described here.

Tags: #GitHub #VSCode #Python #NodeJS #DeveloperTools #Conda #ExtensionDevelopment #JavaScript #Productivity #OpenSource

Top comments (0)