How much can i delete a file from a git branch?

There are many threads in SO about tracking different files in different branches. Since git eases deployment too, people try to add deployment specific secret files to an in-house branch and the problem discussed in those threads will arise. One possible suggested solution is using submodule. I prefer to avoid it. So by extending this method made a script that let me merge a private branch without considering changes in secret files listed in .branchownfiles.

It seems work well, but raise a question about file removal in git: git merge complains about deleted files when merging from private to master, even if this file never had participated in a commit related to master: “file deleted in HEAD and modified in private”

This is the procedure you can see:

i@my % git init
Initialized empty Git repository in /here/there/.git/
i@my % echo 'hello' > 'start.txt'
i@my % git add start.txt
i@my % git commit -m 'started'
[master (root-commit) 32d41f5] started
 1 file changed, 1 insertion(+)
 create mode 100644 start.txt

i@my % git checkout -b private
Switched to a new branch 'private'
i@my % echo 'this is secret' > a_secret.txt
i@my % echo a_secret.txt > .branchownfiles
i@my % echo .branchownfiles >> .branchownfiles
i@my % git add a_secret.txt
i@my % git add .branchownfiles
i@my % git commit -m 'a secret file added'
[private c2e174f] a secret file added
 2 files changed, 3 insertions(+)
 create mode 100644 .branchownfiles
 create mode 100644 a_secret.txt

i@my % git checkout master
Switched to branch 'master'
i@my % git-merge-with-care.sh private
Automatic merge went well; stopped before committing as requested
rm '.branchownfiles'
rm 'a_secret.txt'
Success
# this is basically a `git merge` followed by two `git rm` for two files.
# so i suppose .branchownfiles and a_secret.txt never should leave anything in `master`
# but my assumption is not true..
i@my % git commit
[master 916c14b] Merge branch 'private'

i@my % git checkout private
Switched to branch 'private'
i@my % echo 'changed in private, should change in public too' > start.txt
i@my % echo 'secret changed too' > a_secret.txt
i@my % git add -u
i@my % git commit -m 'secret changed'
[private dc8938a] secret changed
 2 files changed, 2 insertions(+), 2 deletions(-)

i@my % git checkout master
Switched to branch 'master'
i@my % git-merge-with-care.sh private
# now my question arise.
CONFLICT (modify/delete): a_secret.txt deleted in HEAD and modified in private. Version private of a_secret.txt left in tree.
# git merge complains about a_secret.txt an knows it deleted in HEAD. Why?!
Automatic merge failed; fix conflicts and then commit the result.
Trying to fix conflicts for branch limited files
a_secret.txt: [b]
Remerge with ours strategy
CONFLICT (modify/delete): a_secret.txt deleted in HEAD and modified in private. Version private of a_secret.txt left in tree.
Automatic merge failed; fix conflicts and then commit the result.
a_secret.txt: needs merge
rm 'a_secret.txt'
Success

My script still works, because it delete these files before merge. But i want to know why git recalls a_secret.txt though it never was in a commit for this branch? Is there a way to accomplish effect of git merge --no-commit followed by git rm in some way git never recall these deleted files was here before? I mean git merge but with the ability to add just some files to index directory.

And is my approach secure? If i git rm some files and git later recalls these files were here before, git still have some information about these files. What kind of data and where is it? Can someone find content of a_secret.txt by hers access restricted to master branch?

And this is my git-merge-with-care.sh:

#!/bin/env zsh

ls -d .git 2>/dev/null 1>&2
if [ $? != 0 ]; then
    echo "error: run this from root of project"
    exit 1
fi

if [ $# != 1 ]; then
    echo "usage: $0 <private branch>"
    exit 1
fi

current_branch=$(git branch -q | sed -n -e 's/^* //p')
from_branch=$1 #private branch
into_branch=master #public branch

if [ $current_branch != $into_branch ]; then
    echo "error: merge while out of $into_branch branch"
    exit 1
fi

new_files=$(git diff -z --name-only --diff-filter=A $into_branch $from_branch)
changed_files=$(git diff -z --name-only --diff-filter='M|T' $into_branch $from_branch)
branch_files=$(git show $from_branch:.branchownfiles)
git merge --no-commit --no-ff $from_branch

# fix conflict with strategy
if [ $? != 0 ]; then
    echo "Trying to fix conflicts for branch limited files"
    failing=false
    git diff -z --name-only --diff-filter=U  | while IFS= read -r -d $'\0' f; do
        { grep -x -q "$f" <<<"$branch_files" && echo "$f: [b]"; } || { [ $f != ".branchownfiles" ] && echo "$f: [!]" && failing=true; }
    done
    git merge --abort
    if $failing; then
        echo "Merge aborted because of merge conflicts"
        exit 2
    else
        echo "Remerge with ours strategy"
        git merge --no-commit $from_branch -s recursive -X ours --no-ff
        if [ $? != 0 ]; then
            # conflits for deleted files in merger but added again from mergee
            conflicting_files=$(git diff -z --name-only --diff-filter=U)
            # echo "$conflicting_files" | sort
            # echo "$new_files" | sort

            if grep -q "$(echo \"$conflicting_files\" | sort -z)" <<<"$(echo \"$new_files\" | sort -z)"; then
                xargs -0 <<<"$conflicting_files" -I'{}' git rm --ignore-unmatch '{}'
                [[ x`git diff --diff-filter=U` == 'x' ]] || exit 5
            fi
        fi
    fi
fi

# remove files added from private branch but are restricted to private branch
xargs -0 -I'{}' <<<$new_files sh -c "grep -x -q '{}' <<<\"$branch_files\" && [ -e '{}' ] && git rm -rf '{}'"

# bring back public files overwritten bye private branch data
xargs -0 -I'{}' <<<"$changed_files" sh -c "grep -x -q '{}' <<<\"$branch_files\" && git checkout HEAD '{}'"
echo "Success"
exit 0

  • Git commit hell
  • git rebase upside down
  • How can I commit only solved files during merging in git?
  • What is the right way to commit/push when there are conflicts in Git or TortoiseGit?
  • Git: regular merge/rebase of long-living bugfix branches to master
  • Force git to manual merge even in “obvious” situations
  • Patch deployment - git doesn't fetch the latest remote commit
  • Can Git merge --squash preserve commit comments?
  • One Solution collect form web for “How much can i delete a file from a git branch?”

    When you merge two branches both those commits become reachable from the merge commit. So if you have access to master you can reach everything on secret before that merge. This is also why git thinks the files are deleted.

    If I checkout that repo and only get the master branch you can do git checkout master^2 and you are effectively on what is the secret branch.

    Try running git merge-base master private. It will give the “a secret file added” commit. One of the differences between that commit and master are the missing files on master (a_secret, .branchownfiles). So git things they must have been deleted on master, since that commit is an ancestor of master. Which is true. That is where the information comes from.

    Git Baby is a git and github fan, let's start git clone.