Skip to content

Commit 68cbea2

Browse files
committed
Merge ch5 to ch6: [scripts] Add batch merge script
2 parents d069510 + fb789b7 commit 68cbea2

File tree

2 files changed

+355
-0
lines changed

2 files changed

+355
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.*/*
2+
.merge_chain_state
23
!.github/*
34
!.vscode/settings.json
45
.idea

merge_chain.sh

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
STATE_FILE=".merge_chain_state"
5+
6+
usage() {
7+
echo "Usage:"
8+
echo " $0 start <start_branch_id> <end_branch_id> # Start merging"
9+
echo " $0 continue # Continue interrupted merge"
10+
echo " $0 abort # Abort interrupted merge"
11+
echo " $0 status # Check current merge status"
12+
echo " $0 sync <start_branch_id> <end_branch_id> # Sync branches from remote"
13+
echo " $0 push <remote> <start_branch_id> <end_branch_id> # Push local branches to remote"
14+
echo " $0 help # Show detailed help information"
15+
echo ""
16+
echo "Examples:"
17+
echo " $0 sync 1 8 # Sync ch1 to ch8 from remote (for new clones)"
18+
echo " $0 start 3 8 # Start merging from ch3 to ch8"
19+
echo " $0 push origin 3 8 # Push ch3 to ch8 to origin remote"
20+
echo " $0 push upstream 3 8 # Push ch3 to ch8 to upstream remote"
21+
echo " $0 status # Check current merge progress"
22+
echo " $0 continue # Continue merge after resolving conflicts"
23+
echo ""
24+
echo "Merge message format: 'Merge ch{source} to ch{target}: {original message}'"
25+
exit 1
26+
}
27+
28+
show_help() {
29+
echo "Git Branch Chain Merge Tool"
30+
echo "==========================="
31+
echo ""
32+
echo "This tool maintains linear commit relationships across multiple branches."
33+
echo "When you commit to branch k, it automatically merges that commit to all"
34+
echo "branches with numbers greater than k."
35+
echo ""
36+
echo "Command descriptions:"
37+
echo " start <start_id> <end_id> Start chain merge from ch<start_id> to ch<end_id>"
38+
echo " continue Continue interrupted merge process"
39+
echo " abort Abort current merge process"
40+
echo " status Show current merge status and progress"
41+
echo " sync <start_id> <end_id> Sync local branches from remote (for new clones)"
42+
echo " push <remote> <start_id> <end_id> Push local branches to specified remote"
43+
echo " help Show this help information"
44+
echo ""
45+
echo "Git Operations done by the tool:"
46+
echo " For each target branch (start+1, start+2, ..., end):"
47+
echo " 1. git checkout ch{target} # Switch to target branch"
48+
echo " 2. git merge ch{source} -m \"Merge...\" # Merge with custom message"
49+
echo " 3. If conflict: save state and exit for manual resolution"
50+
echo " 4. If success: continue to next branch"
51+
echo ""
52+
echo "Workflow:"
53+
echo " For newly cloned repositories:"
54+
echo " 0. Run '$0 sync 1 8' to create local branches from remote"
55+
echo ""
56+
echo " Normal workflow:"
57+
echo " 1. Commit code to a branch (e.g., ch3)"
58+
echo " 2. Run '$0 start 3 8' to start merging"
59+
echo " 3. If conflicts occur, resolve manually and run '$0 continue'"
60+
echo " 4. Repeat step 3 until all branches are merged"
61+
echo " 5. Run '$0 push origin 3 8' to push all merged branches to remote"
62+
echo ""
63+
echo "Conflict Resolution (when merge fails):"
64+
echo " 1. Edit conflicted files manually"
65+
echo " 2. Stage resolved files with 'git add'"
66+
echo " 3. Complete the merge commit with 'git merge --continue'"
67+
echo " 4. Execute '$0 continue' to resume chain merge process"
68+
echo ""
69+
echo "Merge message format:"
70+
echo " 'Merge ch{source} to ch{target}: {original commit message}'"
71+
echo ""
72+
echo " Where:"
73+
echo " - source: Previous branch number (e.g., ch3)"
74+
echo " - target: Current branch number (e.g., ch4)"
75+
echo " - original commit message: The commit message from the starting branch"
76+
echo ""
77+
echo " Example: 'Merge ch3 to ch4: Add user authentication feature'"
78+
echo ""
79+
echo "State file:"
80+
echo " Creates '.merge_chain_state' file to save progress during merge"
81+
echo " Automatically deleted when merge completes or is aborted"
82+
echo ""
83+
exit 0
84+
}
85+
86+
if [[ $# -lt 1 ]]; then
87+
usage
88+
fi
89+
90+
COMMAND=$1
91+
92+
case "$COMMAND" in
93+
start)
94+
if [[ $# -ne 3 ]]; then usage; fi
95+
START_BRANCH=$2
96+
END_BRANCH=$3
97+
98+
# Validate branch numbers
99+
if [[ ! $START_BRANCH =~ ^[0-9]+$ ]] || [[ ! $END_BRANCH =~ ^[0-9]+$ ]]; then
100+
echo "Error: Branch numbers must be numeric"
101+
exit 1
102+
fi
103+
104+
if [[ $START_BRANCH -ge $END_BRANCH ]]; then
105+
echo "Error: Start branch number must be less than end branch number"
106+
exit 1
107+
fi
108+
109+
110+
# Check if all required branches exist locally
111+
echo "Checking required branches..."
112+
for ((branch_num=START_BRANCH; branch_num<=END_BRANCH; branch_num++)); do
113+
branch_name="ch${branch_num}"
114+
115+
# Check if local branch exists
116+
if ! git show-ref --verify --quiet refs/heads/${branch_name}; then
117+
echo ""
118+
echo "❌ Error: Local branch ${branch_name} does not exist"
119+
echo "💡 Solution: Run the following command to sync branches from remote:"
120+
echo " $0 sync ${START_BRANCH} ${END_BRANCH}"
121+
echo ""
122+
echo "This will create local branches from the remote repository."
123+
exit 1
124+
else
125+
echo "Local branch ${branch_name} exists"
126+
fi
127+
done
128+
129+
# Check for incomplete merge
130+
if [[ -f "$STATE_FILE" ]]; then
131+
echo "Error: Incomplete merge process exists, please run '$0 continue' or '$0 abort' first"
132+
exit 1
133+
fi
134+
135+
NEXT_BRANCH=$((START_BRANCH + 1))
136+
# Get the latest commit message from the first branch
137+
ORIGIN_MSG=$(git log -1 --pretty=%B ch${START_BRANCH})
138+
echo "Starting chain merge: ch${START_BRANCH} -> ch${END_BRANCH}"
139+
echo "Original commit message: $ORIGIN_MSG"
140+
;;
141+
142+
continue)
143+
if [[ ! -f "$STATE_FILE" ]]; then
144+
echo "No interrupted state found, cannot continue."
145+
exit 1
146+
fi
147+
read START_BRANCH END_BRANCH ORIGIN_MSG NEXT_BRANCH < "$STATE_FILE"
148+
echo "Continuing interrupted merge from ch$NEXT_BRANCH..."
149+
;;
150+
151+
abort)
152+
if [[ -f "$STATE_FILE" ]]; then
153+
rm "$STATE_FILE"
154+
echo "Interrupted merge has been aborted."
155+
else
156+
echo "No interrupted state found, nothing to abort."
157+
fi
158+
exit 0
159+
;;
160+
161+
status)
162+
if [[ -f "$STATE_FILE" ]]; then
163+
read START_BRANCH END_BRANCH ORIGIN_MSG NEXT_BRANCH < "$STATE_FILE"
164+
echo "Merge status: In progress"
165+
echo "Start branch: ch${START_BRANCH}"
166+
echo "End branch: ch${END_BRANCH}"
167+
echo "Original message: $ORIGIN_MSG"
168+
echo "Current progress: Preparing to merge to ch${NEXT_BRANCH}"
169+
echo "Remaining branches: $((END_BRANCH - NEXT_BRANCH + 1))"
170+
171+
# Show completed branches
172+
if [[ $NEXT_BRANCH -gt $((START_BRANCH + 1)) ]]; then
173+
echo "Completed: ch${START_BRANCH} -> ch$((NEXT_BRANCH - 1))"
174+
fi
175+
176+
# Show pending branches
177+
if [[ $NEXT_BRANCH -le $END_BRANCH ]]; then
178+
echo "Pending: ch${NEXT_BRANCH} -> ch${END_BRANCH}"
179+
fi
180+
else
181+
echo "Merge status: No merge in progress"
182+
echo "Current branch: $(git branch --show-current)"
183+
fi
184+
exit 0
185+
;;
186+
187+
sync)
188+
if [[ $# -ne 3 ]]; then usage; fi
189+
SYNC_START=$2
190+
SYNC_END=$3
191+
192+
# Validate branch numbers
193+
if [[ ! $SYNC_START =~ ^[0-9]+$ ]] || [[ ! $SYNC_END =~ ^[0-9]+$ ]]; then
194+
echo "Error: Branch numbers must be numeric"
195+
exit 1
196+
fi
197+
198+
if [[ $SYNC_START -gt $SYNC_END ]]; then
199+
echo "Error: Start branch number must be less than or equal to end branch number"
200+
exit 1
201+
fi
202+
203+
# Fetch remote branches
204+
echo "Fetching remote branch information..."
205+
if ! git fetch origin --quiet; then
206+
echo "Error: Failed to fetch from remote repository"
207+
exit 1
208+
fi
209+
210+
# Sync branches
211+
echo "Syncing branches ch${SYNC_START} to ch${SYNC_END}..."
212+
for ((branch_num=SYNC_START; branch_num<=SYNC_END; branch_num++)); do
213+
branch_name="ch${branch_num}"
214+
215+
if ! git show-ref --verify --quiet refs/heads/${branch_name}; then
216+
if git show-ref --verify --quiet refs/remotes/origin/${branch_name}; then
217+
echo "Creating local branch ${branch_name} from origin/${branch_name}..."
218+
if ! git checkout -b ${branch_name} origin/${branch_name}; then
219+
echo "Error: Failed to create local branch ${branch_name}"
220+
exit 1
221+
fi
222+
else
223+
echo "Warning: Remote branch origin/${branch_name} does not exist, skipping"
224+
fi
225+
else
226+
echo "Local branch ${branch_name} already exists"
227+
fi
228+
done
229+
230+
echo "Branch synchronization completed!"
231+
exit 0
232+
;;
233+
234+
push)
235+
if [[ $# -ne 4 ]]; then usage; fi
236+
REMOTE=$2
237+
PUSH_START=$3
238+
PUSH_END=$4
239+
240+
# Validate branch numbers
241+
if [[ ! $PUSH_START =~ ^[0-9]+$ ]] || [[ ! $PUSH_END =~ ^[0-9]+$ ]]; then
242+
echo "Error: Branch numbers must be numeric"
243+
exit 1
244+
fi
245+
246+
if [[ $PUSH_START -gt $PUSH_END ]]; then
247+
echo "Error: Start branch number must be less than or equal to end branch number"
248+
exit 1
249+
fi
250+
251+
# Check for incomplete merge
252+
if [[ -f "$STATE_FILE" ]]; then
253+
echo "Warning: There is an incomplete merge process."
254+
echo "Please run '$0 continue' or '$0 abort' before pushing."
255+
exit 1
256+
fi
257+
258+
# Push branches
259+
echo "Pushing branches ch${PUSH_START} to ch${PUSH_END} to remote '${REMOTE}'..."
260+
FAILED_PUSHES=()
261+
262+
for ((branch_num=PUSH_START; branch_num<=PUSH_END; branch_num++)); do
263+
branch_name="ch${branch_num}"
264+
265+
# Check if local branch exists
266+
if ! git show-ref --verify --quiet refs/heads/${branch_name}; then
267+
echo "❌ Warning: Local branch ${branch_name} does not exist, skipping"
268+
FAILED_PUSHES+=("${branch_name} (not found)")
269+
continue
270+
fi
271+
272+
echo "Pushing ${branch_name}..."
273+
if git push ${REMOTE} ${branch_name}; then
274+
echo "✅ Successfully pushed ${branch_name}"
275+
else
276+
echo "❌ Failed to push ${branch_name}"
277+
FAILED_PUSHES+=("${branch_name} (push failed)")
278+
fi
279+
done
280+
281+
echo ""
282+
if [[ ${#FAILED_PUSHES[@]} -eq 0 ]]; then
283+
echo "🎉 All branches pushed successfully!"
284+
else
285+
echo "⚠️ Push completed with some failures:"
286+
for failure in "${FAILED_PUSHES[@]}"; do
287+
echo " - ${failure}"
288+
done
289+
echo ""
290+
echo "Please check the failed branches and try again if needed."
291+
fi
292+
293+
exit 0
294+
;;
295+
296+
help)
297+
show_help
298+
;;
299+
300+
*)
301+
usage
302+
;;
303+
esac
304+
305+
for ((i=NEXT_BRANCH; i<=END_BRANCH; i++)); do
306+
CUR_BRANCH="ch${i}"
307+
PREV_BRANCH="ch$((i-1))"
308+
PROGRESS="[$((i-START_BRANCH))/$((END_BRANCH-START_BRANCH))]"
309+
310+
echo ""
311+
echo "=== ${PROGRESS} Merging ${PREV_BRANCH} -> ${CUR_BRANCH} ==="
312+
313+
# Check if target branch exists
314+
if ! git show-ref --verify --quiet refs/heads/${CUR_BRANCH}; then
315+
echo "Error: Branch ${CUR_BRANCH} does not exist"
316+
echo "$START_BRANCH $END_BRANCH \"$ORIGIN_MSG\" $i" > "$STATE_FILE"
317+
exit 1
318+
fi
319+
320+
echo "Switching to branch ${CUR_BRANCH}..."
321+
if ! git checkout "${CUR_BRANCH}"; then
322+
echo "Error: Cannot switch to branch ${CUR_BRANCH}"
323+
echo "$START_BRANCH $END_BRANCH \"$ORIGIN_MSG\" $i" > "$STATE_FILE"
324+
exit 1
325+
fi
326+
327+
echo "Merging ${PREV_BRANCH}..."
328+
if ! git merge "${PREV_BRANCH}" -m "Merge ${PREV_BRANCH} to ${CUR_BRANCH}: ${ORIGIN_MSG}"; then
329+
echo ""
330+
echo "❌ Conflict occurred in ${CUR_BRANCH}"
331+
echo "Please follow these steps to resolve:"
332+
echo "1. Manually resolve conflict files"
333+
echo "2. Run 'git add .' to add resolved files"
334+
echo "3. Run 'git commit' to commit the merge"
335+
echo "4. Run '$0 continue' to continue the merge process"
336+
echo ""
337+
echo "Or run '$0 abort' to abort the merge"
338+
echo "Or run '$0 status' to check current status"
339+
echo "$START_BRANCH $END_BRANCH \"$ORIGIN_MSG\" $i" > "$STATE_FILE"
340+
exit 1
341+
fi
342+
343+
echo "✅ Successfully merged to ${CUR_BRANCH}"
344+
done
345+
346+
# Merge completed, delete state file
347+
[[ -f "$STATE_FILE" ]] && rm "$STATE_FILE"
348+
echo ""
349+
echo "🎉 All branches merged successfully!"
350+
echo "Merge range: ch${START_BRANCH} -> ch${END_BRANCH}"
351+
echo "Original commit: $ORIGIN_MSG"
352+
echo ""
353+
echo "💡 To push all merged branches to remote, run:"
354+
echo " $0 push origin ${START_BRANCH} ${END_BRANCH}"

0 commit comments

Comments
 (0)