Managing Merge Conflicts
A comprehensive guide to understanding, resolving, and preventing merge conflicts in Git, with SuiteScript examples.
This guide uses the same team from our SDF + Git Practice Guide:
- Developer A (ryandiocean)
- Developer B (luckyocean)
What is a Merge Conflict?
A merge conflict occurs when Git cannot automatically combine changes because two people modified the same lines in the same file.
Original: log.audit('After Submit', 'Customer saved');
├── ryandiocean changes to: log.debug(...)
└── luckyocean changes to: log.audit(...+ timestamp)
↓
⚠️ CONFLICT!
Understanding Conflict Markers
When a conflict occurs, Git marks the conflicting section in your file:
<<<<<<< HEAD
log.debug('After Submit', 'Customer saved with ID: ' + customerId);
=======
log.audit('After Submit', 'Customer saved with ID: ' + customerId + ' at ' + new Date());
>>>>>>> bugfix/NS-790-timestamp
Breakdown:
| Marker | Meaning |
|---|---|
<<<<<<< HEAD | Start of conflict - YOUR current branch (ryandiocean's changes) |
log.debug(...) | Your code - changed to debug level |
======= | Separator between versions |
log.audit(... + new Date()) | Their code - luckyocean added timestamp |
>>>>>>> bugfix/NS-790-timestamp | End of conflict - incoming branch name |
How to Resolve Conflicts (Step-by-Step)
| Step | Action | Commands/Tools |
|---|---|---|
| 1. DETECT | Conflict occurs during merge | git status to see files |
| 2. REVIEW | Open file in VSCode | Find <<<<<<< markers |
| 3. RESOLVE | Decide what to keep | Remove all markers |
| 4. COMPLETE | Test, stage, commit | git add → git commit |
Practical Example: User Event Script Conflict
Scenario: ryandiocean and luckyocean both modified the same User Event script.
The Setup
ryandiocean created bugfix/NS-789-log-level to change log level:
// Changed log.audit to log.debug
log.debug('After Submit', 'Customer saved with ID: ' + customerId);
luckyocean created bugfix/NS-790-timestamp to add timestamp:
// Added timestamp to existing log
log.audit('After Submit', 'Customer saved with ID: ' + customerId + ' at ' + new Date());
Both modified the same line in user_event_customer.js.
Step 1: ryandiocean merges first (success)
git checkout develop
git pull origin develop
git merge bugfix/NS-789-log-level
git push origin develop
Step 2: luckyocean attempts merge (CONFLICT!)
git checkout develop
git pull origin develop
git merge bugfix/NS-790-timestamp
# Output:
# Auto-merging src/FileCabinet/SuiteScripts/user_event_customer.js
# CONFLICT (content): Merge conflict in src/FileCabinet/SuiteScripts/user_event_customer.js
# Automatic merge failed; fix conflicts and then commit the result.
Step 3: Check conflict status
git status
# Output:
# On branch develop
# You have unmerged paths.
#
# Unmerged paths:
# (use "git add <file>..." to mark resolution)
# both modified: src/FileCabinet/SuiteScripts/user_event_customer.js
Step 4: Open in VSCode
VSCode highlights conflicts with colors:
- Green = Current (ryandiocean's debug change)
- Blue = Incoming (luckyocean's timestamp)
VS Code buttons above conflict:
| Button | Action |
|---|---|
| Accept Current Change | Keep debug level only |
| Accept Incoming Change | Keep audit + timestamp only |
| Accept Both Changes | Include both lines (causes error!) |
| Compare Changes | View side-by-side |
Step 5: The Conflict in the File
/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
define(['N/log'], function(log) {
function afterSubmit(context) {
var customer = context.newRecord;
var customerId = customer.id;
<<<<<<< HEAD
log.debug('After Submit', 'Customer saved with ID: ' + customerId);
=======
log.audit('After Submit', 'Customer saved with ID: ' + customerId + ' at ' + new Date());
>>>>>>> bugfix/NS-790-timestamp
}
return { afterSubmit: afterSubmit };
});
Step 6: Resolution Options
Option A: Accept Current (debug level, no timestamp)
log.debug('After Submit', 'Customer saved with ID: ' + customerId);
Option B: Accept Incoming (audit level with timestamp)
log.audit('After Submit', 'Customer saved with ID: ' + customerId + ' at ' + new Date());
Option C: Manual Edit - BEST (combine both: debug + timestamp)
// Combine both changes: use debug level AND add timestamp
log.debug('After Submit', 'Customer saved with ID: ' + customerId + ' at ' + new Date());
Step 7: Remove ALL markers and save
Final clean code:
/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
define(['N/log'], function(log) {
function afterSubmit(context) {
var customer = context.newRecord;
var customerId = customer.id;
// Combined: debug level (NS-789) + timestamp (NS-790)
log.debug('After Submit', 'Customer saved with ID: ' + customerId + ' at ' + new Date());
}
return { afterSubmit: afterSubmit };
});
Step 8: Validate and complete merge
# Validate with SDF
Ctrl+Shift+P → SuiteCloud: Validate Project
# Stage the resolved file
git add src/FileCabinet/SuiteScripts/user_event_customer.js
# Complete the merge
git commit -m "Merge bugfix/NS-790: Resolved conflict - debug with timestamp
Combined changes from both branches:
- NS-789: Changed log level from audit to debug
- NS-790: Added timestamp to log message"
git push origin develop
Using VSCode Merge Editor (3-Way Merge)
VSCode has a powerful built-in merge editor:
| Panel | Shows |
|---|---|
| INCOMING (theirs) | Changes from the branch you're merging |
| CURRENT (yours) | Your current branch changes |
| RESULT | Your final merged code |
To open the Merge Editor:
- Click on a conflicted file in Source Control panel
- Click "Resolve in Merge Editor" button
- Use checkboxes to select which changes to include
- Click "Complete Merge" when done
More SuiteScript Conflict Examples
Scheduled Script Conflict
Scenario: ryandiocean and luckyocean both modify the batch processing logic.
The conflict:
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/search', 'N/record', 'N/log'], function(search, record, log) {
function execute(context) {
var invoiceSearch = search.create({
type: 'invoice',
filters: [['status', 'anyof', 'CustInvc:A']],
<<<<<<< HEAD
columns: ['entity', 'total', 'duedate'] // ryandiocean added duedate
=======
columns: ['entity', 'total', 'trandate', 'memo'] // luckyocean added trandate, memo
>>>>>>> feature/NS-567-add-columns
});
// ... rest of script
}
return { execute: execute };
});
Resolution - Combine both column additions:
columns: ['entity', 'total', 'duedate', 'trandate', 'memo']
Client Script Conflict
Scenario: Both developers modify the fieldChanged function.
The conflict:
/**
* @NApiVersion 2.1
* @NScriptType ClientScript
*/
define(['N/currentRecord', 'N/dialog'], function(currentRecord, dialog) {
function fieldChanged(context) {
var fieldId = context.fieldId;
<<<<<<< HEAD
// ryandiocean: Added customer validation
if (fieldId === 'entity') {
var customerId = context.currentRecord.getValue({ fieldId: 'entity' });
if (!customerId) {
dialog.alert({ title: 'Warning', message: 'Please select a customer' });
}
}
=======
// luckyocean: Added item quantity check
if (fieldId === 'quantity') {
var qty = context.currentRecord.getCurrentSublistValue({
sublistId: 'item',
fieldId: 'quantity'
});
if (qty > 100) {
dialog.alert({ title: 'Warning', message: 'Quantity exceeds limit' });
}
}
>>>>>>> feature/NS-678-quantity-check
}
return { fieldChanged: fieldChanged };
});
Resolution - Keep BOTH validations (they handle different fields):
function fieldChanged(context) {
var fieldId = context.fieldId;
// Customer validation (NS-123 - ryandiocean)
if (fieldId === 'entity') {
var customerId = context.currentRecord.getValue({ fieldId: 'entity' });
if (!customerId) {
dialog.alert({ title: 'Warning', message: 'Please select a customer' });
}
}
// Quantity check (NS-678 - luckyocean)
if (fieldId === 'quantity') {
var qty = context.currentRecord.getCurrentSublistValue({
sublistId: 'item',
fieldId: 'quantity'
});
if (qty > 100) {
dialog.alert({ title: 'Warning', message: 'Quantity exceeds limit' });
}
}
}
When developers added independent functionality (like validations for different fields), you can often keep both changes. Just make sure they don't interfere with each other.
Conflict Resolution by Scenario
Feature Development Conflicts
When it happens: Merging develop into your feature branch before PR
# Before creating PR, always sync with develop
git checkout feature/my-feature
git fetch origin
git merge origin/develop
# If conflicts occur:
# - Usually in files you both modified
# - Often in shared libraries or configs
Best Practice: Sync with develop FREQUENTLY
develop ──●────●────●────●──────
│ ↓ ↓ ↓
feature ──●────●────●────●──→ PR
↑ ↑ ↑
(merge develop frequently)
Small, frequent merges = Small, easy conflicts One big merge at the end = Big, complex conflicts
Common Conflict Files in SDF:
| File Type | Why Conflicts Occur | Resolution Tip |
|---|---|---|
manifest.xml | Multiple scripts added | Include all dependencies |
| Custom Records XML | Multiple field additions | Merge all new fields |
| Shared Libraries | Different utility functions | Keep both functions |
| Config files | Different settings | Discuss with team |
Bugfix Conflicts
When it happens: Your bugfix touches code that changed in develop
git checkout bugfix/my-fix
git merge origin/develop
# Conflict likely in:
# - The file you're fixing
# - Related test files
Resolution Strategy:
- Understand both changes - What did develop add? What is your fix?
- Apply your fix to their code - Usually you need to re-apply your fix to the new code
- Test thoroughly - Make sure both the new feature AND your fix work
Example:
// CONFLICT: You fixed a bug, but they refactored the function
// Their version (develop):
function calculateTax(amount, rate) {
// They refactored to use new parameter
return amount * rate;
}
// Your version (bugfix):
function calculateTax(amount) {
// You fixed the null check
if (!amount) return 0;
return amount * TAX_RATE;
}
// RESOLUTION: Apply your fix to their refactored code
function calculateTax(amount, rate) {
if (!amount) return 0; // Your fix
return amount * rate; // Their refactor
}
Hotfix Conflicts
When it happens: Syncing hotfix back to develop
# After merging hotfix to main
git checkout develop
git merge main # Sync the hotfix
# Conflict if develop has diverged significantly
Resolution Strategy:
- Hotfix code takes priority - The fix is critical and tested
- Carefully integrate - Make sure develop's changes don't break the fix
- Test in sandbox - Deploy develop to sandbox and verify fix still works
Preventing Merge Conflicts
Best Practices
| # | Practice | Action | Benefit |
|---|---|---|---|
| 1 | Communicate | Tell team what files you're working on | Others avoid same files |
| 2 | Sync frequently | git merge origin/develop daily | Smaller conflicts |
| 3 | Short branches | 1-3 days ideal, max 1 week | Less divergence |
| 4 | Small commits | One change per commit | Easier to resolve |
| 5 | No reformatting | Don't change formatting in unrelated files | Avoid false conflicts |
| 6 | Separate files | New feature = new file when possible | No overlapping edits |
Commands Reference
| Command | Description |
|---|---|
git status | See which files have conflicts |
git diff | See the conflict details |
git checkout --ours <file> | Keep your version entirely |
git checkout --theirs <file> | Keep their version entirely |
git add <file> | Mark conflict as resolved |
git merge --abort | Cancel the merge and go back |
git reset --hard HEAD | Abandon all changes (use carefully!) |
When to Ask for Help
- You're not sure which code is correct
- The conflict involves code you don't understand
- Multiple files have complex conflicts
- You've been stuck for more than 15 minutes
- The conflict involves critical business logic
Who to ask:
- The person who wrote the conflicting code (check
git blame) - Your IT Lead
- Senior team member
Troubleshooting Common Issues
"I accidentally committed with conflict markers"
# Fix: Amend the commit
# 1. Fix the file (remove markers)
# 2. Stage the fix
git add <file>
# 3. Amend the commit
git commit --amend
"I want to start over"
# Abort the current merge
git merge --abort
# You're back to before the merge
# Try again when ready
"I accepted the wrong changes"
# If you haven't committed yet:
git checkout --conflict=merge <file>
# This restores the conflict markers so you can try again
# If you already committed:
git revert HEAD
# Then redo the merge
"Too many conflicts, this is overwhelming"
# Option 1: Abort and get help
git merge --abort
# Option 2: Resolve one file at a time
git status # List all conflicted files
# Fix one file
git add <file>
# Repeat for each file
# Finally commit when all resolved
Related Documentation
- Git Workflows - Development workflows with PR and direct merge options
- Commands Reference - Common Git commands
- Troubleshooting - Other Git issues