Home > OS >  Git "rebase" but ignoring files in new .gitignore
Git "rebase" but ignoring files in new .gitignore

Time:10-13

Is there a way to replay commits from a source branch into a destination branch but taking into consideration changes made in .gitignore in the destination branch?

Here is the scenario:

Say I branch out of master and start committing files, including .bin files that should've been in .gitignore but weren't. Is there any way I can go to master, commit "*.bin" to .gitignore and then fix my topic branch when rebasing it (or some other automatic operation)? By fixing I mean remove the changesets of any .bin file, which are now ignored. This means 1) if a commit has a change on a.txt and foo.bin it should commit only a.txt and 2) if a commit only has a change on foo.bin it should be dropped altogether

The goal is to easily cleanup multiple mistakes only caught at Pull Request time.

Regular git rebase didn't work. the mistakenly committed file remains committed even if in the (new) linear history of the repo, the gitignore pattern was there before the bad commit

CodePudding user response:

Is there a way to replay commits from a source branch into a destination branch but taking into consideration changes made in .gitignore in the destination branch?

Yes, it's possible, however, it would take some scripting work. (My example script in the loop below might even work for you as is.) Basically you would simulate the rebase but with a few steps in-between each commit. The algorithm would look something like this:

  1. Obtain the list of commits to re-write and store them in an array or list.
  2. Reset your source branch to the target. (The .gitignore file should now be in place.)
  3. For each commit in the list: cherry-pick the commit with the --no-commit flag so you don't finish the commit, then reset to unstage the changes, then add them back which heeds the .gitignore instructions.

Here's a working example bash script (save it as a file and run it in an empty directory to test):

#!/bin/bash -v

git init

git branch -m main # rename to main in case that's not your default branch name

echo asdf > asdf.txt && git add . && git commit -m "Add asdf.txt"

git branch test-branch

echo "*.log" > .gitignore && git add . && git commit -m "Add .gitignore"

git switch test-branch

echo abc > abc.txt
echo def > def.log
git add . && git commit -m "Add abc.txt and def.log"

echo ghi > ghi.txt
echo hij > hij.log
git add . && git commit -m "Add ghi.txt and hij.log"

git log --all --graph --name-only

# Get all the commit IDs that would be rebased if we rebased test-branch onto main,
# and store them into an array in reverse order
declare -a commitIDs=(`git log main..test-branch --pretty=format:"%h" --reverse`)

# reset test-branch to main
git reset --hard main

# loop through the commitIDs and cherry-pick each one without committing
for i in "${commitIDs[@]}"
do
   echo "About to cherry-pick commit $i"
   git cherry-pick $i --no-commit # this leaves the commit staged
   git reset # unstage so we can commit with the .gitignore
   git add . # this doesn't add ignored files
   git commit --reuse-message=$i
done

git log --all --graph --name-only

When finished, look at the output of the two git log statements. The first one has 2 commits each with a .txt and .log file in it. The second one uses the .gitignore file to remove all the .log files from those commits.

Note I used the previous commit messages when re-writing since that's usually what you would want. But I purposefully named my commits in such a way to highlight scenarios where you wouldn't want to do this, for example when the file name you are ignoring is specified in the commit message.

CodePudding user response:

No, you need to use git rm to remove files that are you already tracking (or if you want to erase all traces of it git-filter-branch). Here is the relevant bits from the gitignore man page (notice the last sentence):

A gitignore file specifies intentionally untracked files that Git should ignore. Files already tracked by Git are not affected

  • Related