All.invoke git branch copy.how to guide
The Git Branch Copy Workflow#
Introduction#
- The
git_branch_copyinvokeworkflow creates a new Git branch derived from the current branch and copies its content exactly - It's primarily useful in scenarios where a large PR (Pull Request) needs to be split into smaller, independently mergeable PRs to improve code review efficiency and reduce integration complexity
- This approach allows to:
- Develop a large feature in one-shot, but merge it in chunks
- Easily extract a sub-PR, since often "removing" code chunks is simpler than "adding" code chunks one by one, e.g., by reverting an entire file
- Overlap developing and testing / reviewing
- Make it simple to extract a sub-PR and then merge it back into the feature branch easily
Workflow Explanation#
- The workflow executes the following operations:
- Cleans up untracked files in the working directory
- Optionally merges the latest changes from
masterbefore creating the new branch - Creates and switches to a new branch
- Automatically generates a suitable branch name if not explicitly provided
- Validates the new branch name according to established naming conventions,
e.g,
{Repo}Task{Number}_{Description}
- Squash merge changes from the current branch into the new branch
- Changes are staged, but uncommitted, to allow developers to selectively stage, commit, and push specific parts.
Usage Instructions#
-
Go to the target branch to merge
> git checkout CmTask5874_Document_PR_flow -
You can execute the workflow with one of these command:
# Create new branch with an automatically generated name (e.g., `CmTask5874_Document_PR_flow_02`). > i git_branch_copy # Create new branch with given name. > i git_branch_copy --new-branch-name="CmTask5874_PR_flow_Images" # Create a new branch without merging `master`. > i git_branch_copy --skip-git-merge-master --new-branch-name="CmTask5874_PR_flow_Images" # Create a new branch without checking for naming conventions. > i git_branch_copy --new-branch-name="wrongname_123" --no-check-branch-name
Example#
- Consider the case where we are working on a large feature branch
CmTask5874_Document_PR_flow -
For various reasons we might want to extract and merge just a few files, e.g.,
- The regression tests are not passing and we want to test and merge only a subset of changes
- The branch contains unrelated changes (e.g., while implementing a feature, we ended up fixing another issue)
- The PR is too complex to review in one shot and we want to break it down in reviewable pieces
-
Rather than merging the main PR, we create one or more children PRs, and merge them into
masterone at a time
Step 1: Make sure your branch is up to date#
-
Use this workflow to ensure your branch is up to date with
master# First switch to your feature branch. > git checkout CmTask5874_Document_PR_flow # Make sure that the branch is up-to-date with master. > i git_merge_master # Commit and push the changes that you have made to the branch. > git commit -m "Merge" > git push # Check the diff between your branch and master. > i git_branch_diff_with -t base --only-print-files -
The output looks like:
INFO: > cmd='/data/sameepp/src/venv/amp.client_venv/bin/invoke git_branch_diff_with -t base --only-print-files' 04:58:35 - INFO lib_tasks_git.py _git_diff_with_branch:726 ############################################################################### # files=3 ############################################################################### 04:58:35 - INFO lib_tasks_git.py _git_diff_with_branch:727 ./figs/development/Fig1.png ./figs/development/Fig2.png docs/work_tools/all.development.how_to_guide.md 04:58:35 - WARN lib_tasks_git.py _git_diff_with_branch:732 Exiting as per user request with --only-print-files -
As shown above, three files have been modified
-
Suppose we only want to partially merge this PR, specifically, just the
.pngfiles, while continuing development in the main feature branch
Step 2: Create a new branch from your feature branch#
-
Create a new branch (e.g.,
CmTask5874_Document_PR_flow_02) derived from our feature branchCmTask5874_Document_PR_flow# Create a derived branch from the feature branch. > i git_branch_copy INFO: > cmd='/data/sameepp/src/venv/amp.client_venv/bin/invoke git_branch_copy' git clean -fd invoke git_merge_master --ff-only From github.com:cryptokaizen/cmamp e59affd79..d6e6ed8e4 master -> master INFO: > cmd='/data/sameepp/src/venv/amp.client_venv/bin/invoke git_merge_master --ff-only' ## git_merge_master: ## git_fetch_master: git fetch origin master:master git submodule foreach 'git fetch origin master:master' git merge master --ff-only Already up to date. 07:04:46 - INFO lib_tasks_git.py git_branch_copy:599 new_branch_name='CmTask5874_Document_PR_flow_2' git checkout master && invoke git_branch_create -b 'CmTask5874_Document_PR_flow_2' Switched to branch 'master' Your branch is up to date with 'origin/master'. INFO: > cmd='/data/sameepp/src/venv/amp.client_venv/bin/invoke git_branch_create -b CmTask5874_Document_PR_flow_2' ## git_branch_create: 07:05:00 - INFO lib_tasks_git.py git_branch_create:413 branch_name='CmTask5874_Document_PR_flow_2' git pull --autostash --rebase Current branch master is up to date. Switched to a new branch 'CmTask5874_Document_PR_flow_2' remote: remote: Create a pull request for 'CmTask5874_Document_PR_flow_2' on GitHub by visiting: remote: https://github.com/cryptokaizen/cmamp/pull/new/CmTask5874_Document_PR_flow_2 remote: To github.com:causify-ai/cmamp.git [new branch] CmTask5874_Document_PR_flow_2 -> CmTask5874_Document_PR_flow_2 git checkout -b CmTask5874_Document_PR_flow_2 git push --set-upstream origin CmTask5874_Document_PR_flow_2 Branch 'CmTask5874_Document_PR_flow_2' set up to track remote branch 'CmTask5874_Document_PR_flow_2' from 'origin'. git merge --squash --ff CmTask5874_Document_PR_flow && git reset HEAD Updating d6e6ed8e4..a264a6f30 Fast-forward Squash commit -- not updating HEAD docs/work_tools/figs/development/Fig1.png | Bin 27415 -> 0 bytes docs/work_tools/figs/development/Fig2.png | Bin 35534 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/work_tools/figs/development/Fig1.png delete mode 100644 docs/work_tools/figs/development/Fig2.png Unstaged changes after reset: D docs/work_tools/figs/development/Fig1.png D docs/work_tools/figs/development/Fig2.png
Step 3: Confirm the new branch is created#
-
After running the command, a new branch
CmTask5874_Document_PR_flow_2is created, containing all changes from the original feature branch> git status On branch CmTask5874_Document_PR_flow_2 Your branch is up to date with 'origin/CmTask5874_Document_PR_flow_2'. Untracked files: (use "git add <file>..." to include in what will be committed) ./figs/development/Fig1.png ./figs/development/Fig2.png docs/work_tools/all.invoke_workflows.how_to_guide.md -
You can now stage and commit only the files you want to merge (e.g., the
.pngfiles), and proceed to create a PR to merge those changes intomaster# Add, commit and push only the required files. > git add ./figs/development/Fig1.png ./figs/development/Fig2.png > git commit -m "Checkpoint" > git push origin CmTask5874_Document_PR_flow_2 -
From this you can create a PR, test it, review it, and merge it
-
Once the chunk is merged into
master, you can go to the father branch and mergemaster> git checkout CmTask5874_Document_PR_flow > i git_merge_master > git commit -am "Merge" > git push -
Note that the merge it's typically very simple, since the incoming code is the same one that is already in the branch
Execution flow diagram#
%%{init: {'theme': 'base', 'gitGraph': {'mainBranchName': 'master'}}}%%
gitGraph
commit id: "Initial commit (master)"
branch CmTask5874_Document_PR_flow
checkout CmTask5874_Document_PR_flow
commit id: "Feature change 1"
commit id: "Feature change 2"
checkout master
branch CmTask5874_Document_PR_flow_2
checkout CmTask5874_Document_PR_flow_2
merge CmTask5874_Document_PR_flow id: "Squash feature changes"
commit id: "Commit .png files only"
checkout master
merge CmTask5874_Document_PR_flow_2 id: "Partial PR merged"
checkout CmTask5874_Document_PR_flow
merge master id: "Sync with master"
Alternative Manual Workflow#
- You can also manually extract part of a PR using patch-based workflows or squash merging.
Using invoke git_create_patch#
-
If you're working with multiple Git clients or prefer working outside your development tree:
# In your feature branch: > i git_patch_create --branch # Copy the patch file to a clean Git client or switch to `master` > git checkout master # Apply the patch generated earlier > git apply ~/patch.amp.8f9cda97.20210609_080439.patch # The patch should apply cleanly. If you get conflicts, your feature branch may not be up-to-date with `master`. # Clean up unwanted files before committing > git diff > git checkout master -- files/to/revert # Commit and push selected changes > git commit -m "Partial merge from feature branch" > git push # Create a PR to trigger GitHub tests > i gh_create_pr --no-draft # Run regression tests > i run_fast_tests # Merge PR into `master` # Return to your feature branch and merge updates from `master` > git checkout CmTask5874_Document_PR_flow > git pull
Known Limitations#
- Currently, the
use_patchoption for creating branches via patches is unimplemented and reserved for future enhancements - The workflow doesn't explicitly handle merge conflicts during the squash merge step, manual resolution may be required
Future Improvements#
- Implement the
use_patchoption to support patch-based branch creation - Introduce enhanced automation for conflict resolution and interactive conflict handling during branch operations
- Provide options to handle specific file types or directories selectively during branch creation