244

.gitignore can ignore whole files, but is there a way to ignore specific lines of code while coding?

I frequently and repeatedly add the same debug lines in a project, only to have to remember to remove them before committing. I'd like to just keep the lines in the code and have git disregard them.

Kache
  • 15,647
  • 12
  • 51
  • 79
  • 139
    I can't tell whether this is a terrible idea or a brilliant one. – Kyle Strand May 01 '13 at 06:53
  • 1
    @KyleStrand at the very least, I can add it to debug lines I type and feel safe in knowing they won't be accidentally committed – Kache May 01 '13 at 21:29
  • 8
    Right, that's the "brilliant" part. – Kyle Strand May 01 '13 at 21:29
  • Maybe I'd feel more comfortable with it if it could only be applied to print lines, or something. And I suppose that if there's a unit testing infrastructure in place that runs after each commit, this would be pretty safe. – Kyle Strand May 01 '13 at 21:35
  • Oh, then you could just change the `sed` regex – Kache May 01 '13 at 23:12
  • @Miguelos Ha, it looks like it is a duplicate. Although the question is much older, the bounty-winning answer to that question is newer than my question and my answer here. – Kache Mar 31 '15 at 00:13
  • @KyleStrand I have now code in front of me that changes the first line of a properties file with a date each time a project is saved. For this scenario, it would be brilliant. If writing a date line is good practice, that's an whole other matter though :) – progonkpa Jan 10 '19 at 08:36
  • @progonkpa I would say that any time you find this feature useful on a _recurring_ basis, it's probably a "config" smell. For instance, at my work we currently have a C# solution with a project that needs to be reconfigured to use the release version of a particular C++ dependency in order to run, even in debug mode, but the C++ project is part of the same MSVS solution, so there doesn't seem to be a clean way to do this without specifying the _path to the compiled DLL_ and _removing_ the dependency on the _project_. But we can't check that change in because it's not correct for deployment. – Kyle Strand Jan 10 '19 at 16:50

2 Answers2

195

This is how you can kind of do it with git filters:

  1. Create/Open gitattributes file:
    • <project root>/.gitattributes (will be committed into repo)
      OR
    • <project root>/.git/info/attributes (won't be committed into repo)
  2. Add a line defining the files to be filtered:
    • *.rb filter=gitignore, i.e. run filter named gitignore on all *.rb files
  3. Define the gitignore filter in your gitconfig:
    • $ git config --global filter.gitignore.clean "sed '/#gitignore$/d'", i.e. delete these lines
    • $ git config --global filter.gitignore.smudge cat, i.e. do nothing when pulling file from repo

Notes:
Of course, this is for ruby files, applied when a line ends with #gitignore, applied globally in ~/.gitconfig. Modify this however you need for your purposes.

Warning!!
This leaves your working file different from the repo (of course). Any checking out or rebasing will mean these lines will be lost! This trick may seem useless since these lines are repeatedly lost on check out, rebase, or pull, but I've a specific use case in order to make use of it.

Just git stash save "proj1-debug" while the filter is inactive (just temporarily disable it in gitconfig or something). This way, my debug code can always be git stash apply'd to my code at any time without fear of these lines ever being accidentally committed.

I have a possible idea for dealing with these problems, but I'll try implementing it some other time.

Thanks to Rudi and jw013 for mentioning git filters and gitattributes.

keineahnung2345
  • 2,635
  • 4
  • 13
  • 28
Kache
  • 15,647
  • 12
  • 51
  • 79
  • 2
    Shouldn't that be: `git config --global filter.gitignore.clean sed '/#gitignore$/d'` since you've named the filter `gitignore` – Onur Yıldırım Dec 16 '13 at 00:51
  • Ah, indeed. In fact, there's also a quotation escaping issue. – Kache Dec 17 '13 at 00:50
  • Can I make it so that it replaces that line with something else? Like lets say I wanted to replace lines ending in #gitignore with "Here's a line of content" – mharris7190 Aug 14 '14 at 20:32
  • 1
    I did this for '*.py'-files and ut worked. Then I did it again for '*.xml'-files. That didn't work, and it also broke the previously working filter for python-files. Now I get this error all the time "error: external filter sed". Do you know how I can fix this or simply delete all filters? – PaulMag Nov 24 '14 at 13:27
  • Hm, I never tried doing this with more than one filetype at once, nor with nesting-format files like XML. In any case: 1 - Remove the lines you added in `.gitattributes` or `.git/info/attributes` that define which filter to use on what type of files. 2 - Open your `.gitconfig` and delete the lines where the clean and smudge commands are that filter. – Kache Nov 24 '14 at 19:37
  • Great answer! Maybe one could have the `smudge` filter check whether the already present old file would have ignored lines `clean`ed out and in that case prompt (to `stderr`) to mention either the removed lines or if somehow possible even manually do the `stash apply`ish thing... – Tobias Kienzler Jan 04 '16 at 07:49
  • Where are the line numbers to ignore being specified ? – happybuddha Sep 27 '16 at 00:59
  • 5
    This is interesting. But it basically acts like the line in question isn't there. It won't work for something like simply making *the changes* to a line be ignored. So you can't, for example, change the value of a variable in a function call. Eg, instead of changing `flags = foo | bar` to `flags = foo | bar | debug_thingy`, you'd have to make sure you add a completely new line like `flags |= debug_thingy` after. – Kat May 25 '18 at 20:27
  • So a open question remains: How to make it to work on line changes instead of only to line additions? – Jp_ Mar 04 '21 at 13:08
  • 3
    I found a solution that works for me. I'm using `git config --global filter.gitignore.clean "sed 's/.*gitignore//'"` and replacing code like this `, var x = replaced #gitignore var x = 42`. So the content after the `#gitignore` replaces the line, so the indentation must be kept after the `#gitignore`. – Jp_ Mar 04 '21 at 13:34
  • 1
    Here's also a sed command to ignore blocks of code `"sed '/^# start git-block-ignore/,/^# end git-block-ignore/d;'"` – Jp_ Mar 04 '21 at 14:31
  • I used this feature and it worked. Later I started having issues with `revert`, `restore`, `reset`, and `reset --hard`. The problem was solved after deleting `.git/info/attributes` Is there any cleaner way to do this? – ilciavo Aug 23 '22 at 08:42
66

I had a similar issue writing java code. My solution was to markup code that I didn't want to commit and then add a pre-commit hook that would look for my markup:

#!/bin/bash
#
# This hook will look for code comments marked '//no-commit'
#    - case-insensitive
#    - dash is optional
#    - there may be a space after the //
#
noCommitCount=$(git diff --no-ext-diff --cached | egrep -i --count "(@No|\/\/\s?no[ -]?)commit")
if [ "$noCommitCount" -ne "0" ]; then
   echo "WARNING: You are attempting to commit changes which include a 'no-commit'."
   echo "Please check the following files:"
   git diff --no-ext-diff --cached --name-only -i -G"(@no|\/\/s?no-?)commit" | sed 's/^/   - /'
   echo
   echo "You can ignore this warning by running the commit command with '--no-verify'"
   exit 1
fi
Mike
  • 1,041
  • 9
  • 17