Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git rebasing attribute the change to the wrong file

Tags:

git

git-rebase

I think I found a bug in git, where a change gets attributed to the wrong file when doing a rebasing, is there any known workaround ?

Short summary

Assume master_branch

.
└── parent_folder
    ├── project_a
    │   └── file.txt 
    └── project_b
        └── file.txt # the 2 file.txt have the same content on master

Assume feature_branch from master

.
└── parent_folder
    ├── project_a
    │   └── file.txt
    └── project_b
        └── file.txt # this file got modified with a feature

Assume refactor_branch from master

.
└── refactored_parent_folder # got renamed
    ├── project_a
    │   └── file.txt
    └── project_b
        └── file.txt 

then rebase feature_branch on refactor_branch,

.
└── refactored_parent_folder 
    ├── project_a
    │   └── file.txt # the feature got moved here !
    └── project_b
        └── file.txt # instead of here, where it should be !

Reproduction bash script

#!/usr/bin/env bash

# tested on linux, git version 2.34.1, 2.43.2.

set -ex
# create a master branch
mkdir -p git_repo
cd git_repo
git init
mkdir -p parent_folder/project_a
echo -e "1\n2\n3" > parent_folder/project_a/file.txt
cp -r parent_folder/project_a parent_folder/project_b
git add .
git commit -m "initial commit"
# we create a feature_branch from master
git branch "feature_branch"

# we create a refactoring_branch from master
git checkout -b "refactoring_branch"

git mv parent_folder refactored_parent_folder
git add .
git commit -m "refactor"

# we create a new feature in project_b
git checkout "feature_branch"
echo -e "1\n2_some_new_feature_in_project_b\n3" > parent_folder/project_b/file.txt
git add .
git commit -m "new feature"

# we rebase 
git rebase refactoring_branch
cat refactored_parent_folder/project_a/file.txt
# refactored_parent_folder/project_a/file.txt contains the line 2_some_new_feature_in_project_I think I found a bug in gitb, which is WRONG ! 
# It should have ended  up in refactored_parent_folder/project_b/file.txt


ENV

tested on linux, git version 2.34.1, 2.43.2 are affected

like image 392
n0tis Avatar asked Oct 30 '25 10:10

n0tis


1 Answers

To clarifies the comments, the bug report is on the Git mailing list here.

It states that during a rebase in Git, where one parent folder was renamed (parent_folderrefactored_parent_folder), and two originally identical file.txt files existed in separate subfolders project_a and project_b, Git incorrectly attributed the feature changes to the project_a file instead of project_b.

Elijah Newren on the Git mailing list explained that this is not a bug per se, but a limitation of Git's rename detection. Git does not store rename information; it heuristically infers renames by comparing file contents and partially considering file basenames.

When multiple files share identical or nearly identical content, Git sees multiple equally valid rename targets and might "guess" incorrectly, picking one target arbitrarily.

The article mentioned by Elijah is the Git Rev News: Edition 107 (January 31st, 2024) It details rename-detection corner cases, which includes that identical files can produce unexpected matches. Some users argue that Git could error out or prefer matching files with the same basename, but, in practice, Git picks any "most similar" file.
But storing or tracking renames explicitly would require fundamental model changes to Git's object database and would potentially degrade performance.


I tested (with git version 2.47.1.windows.1) a git rebase --no-renames: this option disables rename detection during the rebase, which can lead to more conflicts but should at least avoid misattributions.

git rebase --strategy-option=no-renames refactor_branch

But… no luck:

git rebase --strategy-option=no-renames refactoring_branch
Successfully rebased and updated refs/heads/feature_branch.
+ cat refactored_parent_folder/project_a/file.txt
1
2_some_new_feature_in_project_b
3

I also tried and set a rename threshold:

git rebase --strategy-option=find-renames=80 refactor_branch

Same wrong result.

I adjusted merge.renameLimit, unsuccessfully.


If possible, make the two file.txts differ slightly (e.g., an extra line). That can help Git "realize" they are not the same file.

echo -e "1\n2\n3\n4\n" > parent_folder/project_a/file.txt
cp -r parent_folder/project_a parent_folder/project_b
echo -e "1\n2\n3\n" > parent_folder/project_b/file.txt
...
git rebase refactoring_branch

cat refactored_parent_folder/project_a/file.txt
cat refactored_parent_folder/project_b/file.txt

I do get the "right" result:

git rebase refactoring_branch
Successfully rebased and updated refs/heads/feature_branch.

+ cat refactored_parent_folder/project_a/file.txt
1
2
3
4

+ cat refactored_parent_folder/project_b/file.txt
1
2_some_new_feature_in_project_b
3
like image 199
VonC Avatar answered Nov 02 '25 02:11

VonC