Skip to main content

Managing Merge Conflicts

A comprehensive guide to understanding, resolving, and preventing merge conflicts in Git, with SuiteScript examples.

Practice Team

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:

MarkerMeaning
<<<<<<< HEADStart 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-timestampEnd of conflict - incoming branch name

How to Resolve Conflicts (Step-by-Step)

StepActionCommands/Tools
1. DETECTConflict occurs during mergegit status to see files
2. REVIEWOpen file in VSCodeFind <<<<<<< markers
3. RESOLVEDecide what to keepRemove all markers
4. COMPLETETest, stage, commitgit addgit 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:

ButtonAction
Accept Current ChangeKeep debug level only
Accept Incoming ChangeKeep audit + timestamp only
Accept Both ChangesInclude both lines (causes error!)
Compare ChangesView 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:

PanelShows
INCOMING (theirs)Changes from the branch you're merging
CURRENT (yours)Your current branch changes
RESULTYour final merged code

To open the Merge Editor:

  1. Click on a conflicted file in Source Control panel
  2. Click "Resolve in Merge Editor" button
  3. Use checkboxes to select which changes to include
  4. 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 to Accept Both

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)
tip

Small, frequent merges = Small, easy conflicts One big merge at the end = Big, complex conflicts

Common Conflict Files in SDF:

File TypeWhy Conflicts OccurResolution Tip
manifest.xmlMultiple scripts addedInclude all dependencies
Custom Records XMLMultiple field additionsMerge all new fields
Shared LibrariesDifferent utility functionsKeep both functions
Config filesDifferent settingsDiscuss 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:

  1. Understand both changes - What did develop add? What is your fix?
  2. Apply your fix to their code - Usually you need to re-apply your fix to the new code
  3. 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:

  1. Hotfix code takes priority - The fix is critical and tested
  2. Carefully integrate - Make sure develop's changes don't break the fix
  3. Test in sandbox - Deploy develop to sandbox and verify fix still works

Preventing Merge Conflicts

Best Practices

#PracticeActionBenefit
1CommunicateTell team what files you're working onOthers avoid same files
2Sync frequentlygit merge origin/develop dailySmaller conflicts
3Short branches1-3 days ideal, max 1 weekLess divergence
4Small commitsOne change per commitEasier to resolve
5No reformattingDon't change formatting in unrelated filesAvoid false conflicts
6Separate filesNew feature = new file when possibleNo overlapping edits

Commands Reference

CommandDescription
git statusSee which files have conflicts
git diffSee 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 --abortCancel the merge and go back
git reset --hard HEADAbandon all changes (use carefully!)

When to Ask for Help

Get Help When:
  • 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:

  1. The person who wrote the conflicting code (check git blame)
  2. Your IT Lead
  3. 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