How to copy one folder from other Git branch and keep commit history?

Summary

The problem arises when trying to copy a folder from one Git branch to another while preserving the commit history, especially after a rename operation has occurred on the target branch. Git’s ability to track file movements complicates direct copying or rebasing approaches.

Root Cause

The root cause of this issue is:

  • Rename operation: The folder FolderB was renamed to FolderC on the master branch using git mv.
  • Divergent histories: The dev2 branch and master branch have diverged, with FolderB existing only on dev2.
  • Git’s history tracking: Git’s mechanism to track file renames and movements interferes with simple copying or rebasing solutions.

Why This Happens in Real Systems

This scenario occurs in real-world systems due to:

  • Evolving project structures: Projects often undergo restructuring, leading to file and folder renames.
  • Branching and merging: The use of branching models for parallel development can lead to divergent histories.
  • Complexity in Git workflows: Understanding and managing Git’s tracking of file movements is crucial for maintaining a clean and coherent project history.

Real-World Impact

The impact of this issue includes:

  • Inability to directly copy: The straightforward copying of a folder from one branch to another is blocked by Git due to its tracking of rename operations.
  • Loss of commit history: Simply copying the files without considering Git’s tracking can result in the loss of commit history for the affected files.
  • Rebasing challenges: Rebasing attempts may fail due to Git recognizing the rename and attempting to apply the changes on top of the renamed version, rather than the original.

Example or Code

# Example steps to visualize the problem:
# Initial setup
git init
mkdir FolderA FolderB
touch FolderA/a.txt FolderB/b.txt
git add.
git commit -m "Initial commit"

# Create dev2 branch
git branch dev2

# Switch to master and rename FolderB
git checkout master
git mv FolderB FolderC
git commit -m "Renamed FolderB to FolderC"

# Switch to dev2 and make changes to FolderB
git checkout dev2
touch FolderB/newfile.txt
git add.
git commit -m "Added new file to FolderB"

# Attempting to rebase or cherry-pick changes from dev2 to master will fail due to rename detection

How Senior Engineers Fix It

Senior engineers address this by:

  • Using Git’s cherry-pick with strategies: Employing git cherry-pick with strategies to handle rename conflicts, possibly using -X options to favor one side’s changes.
  • Manual resolution: Manually resolving conflicts and adjusting the commit history to correctly reflect the intended changes and folder structure.
  • Rebase with handling for renames: Using git rebase with appropriate handling for renames, potentially involving manual intervention to correct the folder structure and commit history.

Why Juniors Miss It

Junior engineers might miss the solution due to:

  • Lack of understanding of Git’s rename tracking: Not grasping how Git handles file and folder renames can lead to attempts at direct copying or rebasing without accounting for rename conflicts.
  • Inexperience with Git workflows and tools: Limited experience with Git’s advanced features, such as cherry-picking with strategies or manual conflict resolution, can hinder their ability to resolve these issues efficiently.
  • Insufficient practice with complex Git scenarios: Not having encountered or practiced resolving similar complex Git scenarios means they are less likely to know the appropriate strategies for handling such problems.