Skip to content

Commit cc9d22d

Browse files
authored
Merge pull request #1 from skyhook-io/fix-prevent-conflict-markers-in-commits
feat: enhance conflict resolution and safety checks
2 parents 0c8ff14 + 057029f commit cc9d22d

2 files changed

Lines changed: 98 additions & 6 deletions

File tree

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,12 @@ The action follows this workflow:
112112
2. **Stash changes** - Safely stashes any uncommitted changes (including untracked files)
113113
3. **Sync with upstream** - Pulls latest changes using rebase to maintain linear history
114114
4. **Restore changes** - Pops the stash to restore your changes
115+
- **Automatic conflict resolution**: If conflicts occur between stashed changes and pulled changes, the action automatically resolves them by accepting your stashed changes (the newer modifications from this workflow)
116+
- This ensures concurrent workflow changes are properly merged without leaving conflict markers
115117
5. **Stage files** - Adds files matching the specified pattern
116-
6. **Commit** - Creates a commit if there are staged changes
117-
7. **Push with retry** - Attempts to push with automatic retry on failure:
118+
6. **Safety check** - Verifies no conflict markers are present in staged files (additional safeguard)
119+
7. **Commit** - Creates a commit if there are staged changes
120+
8. **Push with retry** - Attempts to push with automatic retry on failure:
118121
- On push failure, rebases again and retries
119122
- Continues up to `max_retries` attempts
120123
- Handles race conditions from concurrent workflows
@@ -297,6 +300,14 @@ This usually means concurrent changes occurred. The action automatically handles
297300
max_retries: 10 # Increase for high-concurrency scenarios
298301
```
299302

303+
### Conflict markers in committed files
304+
305+
The action now includes automatic conflict resolution and a safety check to prevent committing conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`). If you see the error "Conflict markers detected in staged files!", this means:
306+
1. A merge conflict occurred that couldn't be auto-resolved
307+
2. The action prevented committing corrupted files
308+
3. Review the workflow logs to understand the conflict
309+
4. This is a safety feature to protect your repository from corrupted files
310+
300311
### No commit created but expected changes
301312

302313
Check that:

action.yml

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,69 @@ runs:
8484

8585
# Stash any uncommitted changes (including untracked files)
8686
echo "📦 Stashing current changes..."
87-
git stash push -u -m "temp-stashed-changes" || true
87+
STASH_RESULT=0
88+
git stash push -u -m "temp-stashed-changes" || STASH_RESULT=$?
89+
90+
if [ $STASH_RESULT -ne 0 ]; then
91+
# Check if it's because there's nothing to stash (expected) or a real error
92+
if git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then
93+
echo "ℹ️ Nothing to stash"
94+
STASH_RESULT=0
95+
else
96+
echo "::error::Failed to stash changes"
97+
exit 1
98+
fi
99+
fi
88100

89101
# Pull latest changes with rebase
90102
echo "⬇️ Pulling latest changes with rebase..."
91103
git pull --rebase origin "$(git branch --show-current)"
92104

93-
# Pop the stash to restore changes
105+
# Pop the stash to restore changes (if we stashed anything)
94106
echo "📂 Restoring stashed changes..."
95-
git stash pop || true
107+
if [ "$(git stash list | grep -c 'temp-stashed-changes')" -gt 0 ]; then
108+
if ! git stash pop; then
109+
echo "⚠️ Merge conflicts detected during stash pop"
110+
111+
# Get list of conflicted files
112+
CONFLICTED_FILES=$(git diff --name-only --diff-filter=U)
113+
114+
if [ -n "$CONFLICTED_FILES" ]; then
115+
echo "🔧 Resolving conflicts by accepting stashed changes..."
116+
117+
# For each conflicted file, accept the stashed version
118+
# In stash pop context: --theirs = stashed changes (what we want)
119+
RESOLUTION_FAILED=0
120+
while IFS= read -r file; do
121+
if [ -n "$file" ]; then
122+
echo " - Resolving: $file"
123+
if ! git checkout --theirs "$file"; then
124+
echo "::error::Failed to resolve conflict in $file"
125+
RESOLUTION_FAILED=1
126+
break
127+
fi
128+
fi
129+
done <<< "$CONFLICTED_FILES"
130+
131+
if [ $RESOLUTION_FAILED -ne 0 ]; then
132+
# Drop the stash to clean up
133+
git stash drop
134+
exit 1
135+
fi
136+
137+
# Drop the stash since we've successfully applied it
138+
git stash drop
139+
140+
echo "✅ Conflicts resolved"
141+
else
142+
echo "ℹ️ No actual conflicts found, continuing..."
143+
# Still need to drop the stash
144+
git stash drop
145+
fi
146+
fi
147+
else
148+
echo "ℹ️ No stash to restore"
149+
fi
96150

97151
# Stage files matching the pattern
98152
echo "➕ Staging files matching pattern: $FILE_PATTERN"
@@ -106,6 +160,20 @@ runs:
106160
exit 0
107161
fi
108162

163+
# Safety check: ensure no conflict markers are being committed
164+
echo "🔍 Checking for conflict markers in staged files..."
165+
if git diff --cached | grep -qE "^\+.*<<<<<<<|^\+.*=======|^\+.*>>>>>>>"; then
166+
echo "::error::Conflict markers detected in staged files! Aborting commit."
167+
echo "The following files contain unresolved conflicts:"
168+
STAGED_FILES=$(git diff --cached --name-only)
169+
while IFS= read -r file; do
170+
if [ -n "$file" ] && grep -qE "<<<<<<<|=======|>>>>>>>" "$file" 2>/dev/null; then
171+
echo " - $file"
172+
fi
173+
done <<< "$STAGED_FILES"
174+
exit 1
175+
fi
176+
109177
# Create commit
110178
echo "💾 Creating commit..."
111179
git commit -m "$COMMIT_MESSAGE"
@@ -127,7 +195,20 @@ runs:
127195

128196
if [ $ATTEMPT -lt $MAX_RETRIES ]; then
129197
echo "⚠️ Push failed, rebasing and retrying..."
130-
git pull --rebase origin "$(git branch --show-current)"
198+
# Use theirs strategy to prefer our committed changes over remote changes during rebase
199+
# In rebase context: theirs = our local commit (what we want to keep)
200+
if ! git pull --rebase -X theirs origin "$(git branch --show-current)"; then
201+
echo "::error::Rebase failed during retry despite conflict resolution strategy."
202+
echo "::error::This indicates a complex conflict that cannot be auto-resolved."
203+
204+
# Check if we're in a rebase state
205+
if [ -d .git/rebase-merge ] || [ -d .git/rebase-apply ]; then
206+
echo "Aborting rebase..."
207+
git rebase --abort
208+
fi
209+
210+
exit 1
211+
fi
131212
fi
132213

133214
ATTEMPT=$((ATTEMPT + 1))

0 commit comments

Comments
 (0)