15

I have cloned a remote SVN repository with git-svn. I have modified a pom.xml file in this cloned repo in a way that the code compiles. This setup is exclusive for me. Thus I don't want to push the changes back on the remote repo.

Is there a way to prevent this (partial) change of a file from being committed into the repo? I'm aware of the fact, that I could use a personal branch, but this would mean certain merging overhead. Are there other ways?

I've looked into this question and this one, but they are for rather temporal changes.

Update: I'm also aware of the .gitignore possibilities, but this would mean to exclude the file completely.

Community
  • 1
  • 1
boutta
  • 24,189
  • 7
  • 34
  • 49
  • 2
    It's not possible to ignore only parts of a file – CharlesB Jun 20 '12 at 08:54
  • 1
    Maybe you should split that xml file up into a user_pom.xml containing your exclusive bits? Sounds like your team members, if there are any, would have the same problem. Alternatively you could track a pom.default.xml file containing non-exclusive data and have git ignore the pom.xml you customized? – mlatu Feb 13 '14 at 09:05
  • I would like just to clarify that adding a file to .gitignore does not mean "to exclude the file completely". It means that the file won't be tracked from now on. Not only it won't be deleted from the repo but also a possibility to commit any changes in it will still be there. Just use `git add IGNORED_FILE`. – Stanowczo Aug 23 '19 at 11:24

6 Answers6

5

EDIT: What you are asking is impossible, I didn't see the "partial" part. I know you can commit only part of files, but you cannot ignore some part of file. You will need to use the update-index trick to avoid having it in the "status" and you will need to stash that file every time you will rebase/merge from the remote, and then unstash your modification and ignore your modification with update-index. I don't know if you can create a git alias for a sequence of git commands so with one command you could do all those 3 commands to avoid the hassle

use a .gitignore file, and don't push it to the remote repo too: Ignore the .gitignore file itself

in your .gitignore, you should have

 .gitignore
 path/to/pom.xml

a .gitignore file can be at the root of the working tree, or in any subdirectory you want/need

Community
  • 1
  • 1
Dolanor
  • 822
  • 9
  • 19
2

There might be a possiblity with filters. But this needs to be explored more.

Starting point: Implement the smudge / clean for this filter:

git config --global filter.versionUpdate.smudge 'sed "s/<version>14.5.0<\/version>/<version>14.5.FEATURE-15<\/version>/"'
git config --global filter.versionUpdate.clean 'sed "s/<version>FEATURE-15<\/version>/<version>14.5.0<\/version>/"'

In this way, the file is always the same when comparing, since the filter is applied.

Problem: It still needs investigation on how to find the filtered files back. This is not so evident in the working directory.

Smudge clean filter

Dimitri Dewaele
  • 10,311
  • 21
  • 80
  • 127
2

It might be easier to solve your problem outside of git. You can define a property with a default value in pom.xml, which everyone uses. And for your exclusive setup you simply override the property in your ${user.home}/.m2/settings.xml or on maven command-line using -Dproperty=value. Or similarly, you can create another profile and activate it based on an environment variable that you export in your, say, ~/.bashrc.

For a git solution, I was accomplishing what you want with some discipline in following the workflow properly:

  1. Make your setup-specific changes to pom.xml (or other files).
  2. Commit the changes into git (locally only). State in the commit title/message that the changes are for local use only, like "LOCAL ONLY. DO NOT DCOMMIT: rest of the title".
  3. Pull changes from svn, make local git commits, etc.
  4. Once it's time to dcommit your changes back to svn, do
    1. git svn rebase to pull new commits from SVN, resolve conflicts.
    2. git rebase -i svn/trunk (or whatever is your svn remote branch) (this will start interactive rebase of all your local changes on top of SVN)
    3. In the interactive rebase todo editor, move your local-only commit to the bottom.
    4. Save the todo and exit the rebase todo editor.
    5. Resolve any conflicts (should be none unless you touched the same part of your pom.xml (or other local-only file) in other local commits).
  5. Run git svn dcommit --dry-run @~1 (or git svn dcommit --dry-run HEAD~1 in older git) to see what's going to be pushed to SVN repository; make sure your local-only commits are not in the list.
  6. Run git svn dcommit @~1 (or git svn dcommit HEAD~1) to finally push your changes to SVN repository.
  7. git svn rebase to pull new commits from SVN.

It may sounds complicated, but it's pretty simple once you get used to it. Basically, you have one (or more) local commits, which you never push to SVN. I haven't had a wrongly pushed commit in more than a year using this workflow with multiple commits and repositories. It's quite useful for having some local debug-only changes, for example (although it's better to introduce an environment variable or some settings file for this).

Furthermore, this can be enhanced with a pre-dcommit hook in git, which would make sure that local-only changes are not being dcommit'ed. Unfortunately, I don't see a standard git-svn hook for this, but one could use git-svn-hooks to implement those.

Andrey
  • 162
  • 1
  • 6
2

One general way to share this kind of configuration file is :

  1. commit a template file (e.g : pom.xml.template)
  2. ignore the file itself (e.g : add pom.xml to the .gitignore file)
  3. have every user customize this file to suit his needs

Some ways to implement step 3. are :

  • have the user manually edit copy the template file and edit it
  • have some script, which applies a substitution on the template file and write the output to the expected target
LeGEC
  • 46,477
  • 5
  • 57
  • 104
2

This sounds to me like a strong case for a pre-commit hook. In your .git folder you should have a hooks folder. It may have some samples in there but they're ignored since they have a .sample extension. If you add a file called 'pre-commit' (no extension) that contains the script below, the script will run each time you make a commit. Don't forget to make the script executable by running "chmod ug+x pre-commit".

I've added some comments explaining what each line does, but essentially how you would use it is you would put '#start' above the code you do not wan't to be included in your git commits, and then '#end' below that code. You can use different start and end flags in the sed command, just make sure you're using the comment syntax for whatever language you're developing in so the compiler ignores them. When you eventually perform a commit, the following will happen:

  1. The code in the middle (including the #start and #end flags) will be removed
  2. The file will be commited without the code and flags
  3. The code and flags will then be returned to your local copy of the file

Since this is all contained in a pre-commit hook, this will all happen automatically each time you commit.

pre-commit

#!/usr/bin/env bash

echo "Running pre-commit hook..."

set -e
export PATH=$PATH:/usr/local/bin
exit_status=0

echo "Removing supercomments..."
# Get a list of files part of this commit
files=$(git diff --cached --name-status)

# Loop through each file
for f in $(echo $files | awk '{ print $2}')
do
        # Create a temp copy of the file
        cp ${f} ${f}.temp
        # Remove all chunks between '#start' and '#end'
        sed -i '/#start/,/#end/d' ${f}
        # Add the file with chunks removed
        git add ${f}
        # Replace file with temp file containing code you didn't want to comm
it
        cp ${f}.temp ${f}
done

echo "Hook completed!"

exit $exit_status

Example Usage:

test.txt

This is a file
It's really just a test file

#start
This code is only relevant to me
When I eventually commit this file...
...please don't inclue all of this!
#end

This part of the file is important,
make sure it's included in my next commit.

add & commit test.txt:

git add test.txt
#

git commit -m "A great commit message."
# Running pre-commit hook...
# Removing supercomments...
# Hook completed!
# [master commit_id_1234] A great commit message.

test.txt (in commit_id_1234)

This is a file
It's really just a test file


This part of the file is important,
make sure it's included in my next commit.

test.txt (local copy after the commit)

This is a file
It's really just a test file

#start
This code is only relevant to me
When I eventually commit this file...
...please don't inclue all of this!
#end

This part of the file is important,
make sure it's included in my next commit.

NOTE: There are some nuances to your question to consider. It sounds like you have a set of code that you want to have in your files but only when compiling locally (you primarily develop and test your code locally I'm assuming), but then you want to be able to commit that development code and automatically have your "only-locally-relevant-code" removed before committing. While the above hook should do just that, keep in mind that the portion of "only-locally-relevant-code" is not being versioned since it's never hitting your remote repository when you eventually push. This may be obvious but just make sure you're OK with this. If something happens to your local copy, all of that code, even though it's only relevant to you, can be lost and can't be recovered by simply re-cloning the repository.

samredai
  • 662
  • 3
  • 8
  • I think this is an awesome answer. I will give you the bounty if you explain me how to prevent the "uncommitted" part of the file to be overwritten by a pull. – Lore Aug 30 '19 at 13:48
0

Good ideas suggested already. Prehooks seem most suitable perhaps. Some other avenues to consider might be: - Fork the repo and commit all changes - Commit a separate branch on same repo and commit that - Have a local branch of just your private diffs which you cherry pick when you check out, and reverse before checking in? - Apply patch files - again which can be reversed before checking in (problematic though)

O Wigley
  • 157
  • 1
  • 4