Can git ignore a specific line?
Asked Answered
O

9

156

I'm using git to sync to phonegap while testing on the phone's native browser. As such I have the following line:

var isPhoneGap = false;

Obviously I change this when building, but is there any way I can set up git to ignore this one line or do I have to go and put it in its own file and ignore it that way?

I'm using Gitx and the terminal on OSX 10.6.

Obelia answered 2/7, 2011 at 14:0 Comment(3)
@user494461: Git filters is the way to do it: https://mcmap.net/q/115890/-how-to-tell-git-to-ignore-individual-lines-i-e-gitignore-for-specific-lines-of-code-duplicateBeck
Can you not detect the enviroment and use an eniroment config file ?Crafty
In short: No. But I've written a preprocessor script that looks for certain commented code and removes it before publication to git. Check it out here: github.com/franklinchou/ahk_config/blob/master/preprocess.shRuscio
C
136

If your file is of a specific type, you can declare a content filter driver, that you can declare in a .gitattributes file (as presented in the "Keyword expansion" of "Git Attributes"):

https://static.mcmap.net/file/mcmap/ZG-Ab5ovKRcpcC0xWRAQWRft/figures/18333fig0702-tn.png

*.yourType filter=yourFilterName

(you can even set that filter for a specific file, if you want)

Implement:

  • yourFilterName.smudge (triggered on git checkout) and

      git config --global filter.yourFilterName.smudge 'sed "s/isPhoneGap = .*/isPhoneGap = true/"'
    
  • yourFilterName.clean (triggered on git add)

      git config --global filter.yourFilterName.clean 'sed "s/isPhoneGap = .*/isPhoneGap = false/"'
    

Your file would appear unchanged on git status, yet its checked out version would have the right value for isPhoneGap.


Note: in the comments, ohaal and Roald suggest to isolate the sed calls in a separate helper.sh script:

sed "s/isPhoneGap = .*/isPhoneGap = true/" "$@"
Chutzpah answered 4/3, 2014 at 11:56 Comment(10)
Thanks for this. got it working. Do you know how to make git diff or git status`, ignore the filters though? So I can still see what is different? My use case is for debug logs... which eventually I want to delete... @jthill @ChutzpahCheri
@Cheri if the filter is working, git status or git diff shouldn't show anything.Chutzpah
it's a pity there is no simpler way in GIT. git ignore <filename> <linenumber> would be so handy!Cupulate
@Cupulate linenumber? What if the line number changes?Chutzpah
This worked great, but Windows users beware, the gitconfig is very picky with which characters it wants to hold, so when trying some semi-exotic regular expressions you may run into trouble. I ended up putting my sed-calls in a separate helper.sh file that I called from my gitconfig with sh ".git/helper.sh" making sure to pass through any parameters to sed with "$@" (I assume just the filepath is passed).Congener
@Congener Good point. Using a script is often easier in those cases.Chutzpah
helper.sh content will then be: sed "s/isPhoneGap = .*/isPhoneGap = true/" "$@"Waldenses
@Waldenses Thank you for this feedback. I have included your comment in the answer for more visibility.Chutzpah
@Chutzpah perhaps comments in code? Filtering seems also dangerous as it can fetch more lines with the same patternCalkins
@JoãoPimentelFerreira I agree: you need a pattern specific enough to pick only what you expect. Not always a guarantee.Chutzpah
C
56

You can use

git update-index --assume-unchanged [file]

to ignore the changes of one file that you don't want track. I use this solution when I need to have a file in the repository but this file have some parts that change that I don't need track always.

When the file have changes that are important you have to do this:

git update-index --no-assume-unchanged [file]

Also, see the Git doc update-index for --[no-]assume-unchanged parameter.

When these flags are specified, the object names recorded for the paths are not updated. Instead, these options set and unset the "assume unchanged" bit for the paths. When the "assume unchanged" bit is on, git stops checking the working tree files for possible modifications, so you need to manually unset the bit to tell git when you change the working tree file.

Carpel answered 11/7, 2012 at 9:58 Comment(5)
Will doing this still fetch the new version when I do git pull?Parachute
@Caffeine when you do git pull you get the last version that was commited, this will be the last before you change to '--assume-unchanged' the file.Carpel
I believe --skip-worktree is a better option for most purposes. https://mcmap.net/q/12408/-git-update-index-assume-unchanged-not-working-for-meNavigate
'Assume-unchanged should not be abused for an ignore mechanism. It is "I know my filesystem operations are slow. I'll promise Git that I won't change these paths by making them with that bit… Especially, it is not a promise by Git that Git will always consider these paths are unmodified—if Git can determine a path that is marked as assume-unchanged has changed without incurring extra lstat(2) cost, it reserves the right to report that the path has been modified (…git commit -a is free to commit that change).'Ambulacrum
this ignores updates to the entire file not a specific lineProclaim
C
8

Continuing https://mcmap.net/q/115890/-how-to-tell-git-to-ignore-individual-lines-i-e-gitignore-for-specific-lines-of-code-duplicate, @Mike proposed to create a pre-commit hook which will grep in the staged files for the lines which one might want to ignore. The hook checks if those lines were staged. If so, it echos a warning and it exit's with code 1 so the commit process won't continue.

Inspired by @Mike's answer, I found myself using perhaps an improved version of his hook which automatically resets (with the -p flag) the specific line which we want to ignore.

I'm not sure this hook will work for a situation where you have many files with this line to be ignored, but this pre-commit hook looks for a changed in this line in a specific file buildVars.java. The hook script looked like this when I tested it on my machine.

#!/bin/sh

# this hook looks for lines with the text `var isPhoneGap = false;` in the file `buildVars.java` and it resets these lines to the previous state before staged with `reset -p`

if [[ $(git diff --no-ext-diff --cached buildVars.java | grep --count -e "var\ isPhoneGap[\ ]*=[\ ]*") -ne 0 ]]; then
    cat <<EOW
WARNING: You are attempting to commit changes which are not supposed to be commited according to this \`pre-commit\` hook
This \`pre-commit\` hook will reset all the files containing this line to it's previous state in the last commit.
EOW
    echo /$'\n'isPhoneGap$'\n'y$'\n'q | git reset -p
    # BONUS: Check if after reseting, there is no actual changes to be commited and if so, exit 1 so the commit process will abort.
    if [[ $(git diff --no-ext-diff --cached | wc -l) -eq 0 ]]; then
        echo there are no actual changes to be commited and besides the change to the variable \'isPhoneGap\' so I won\'t commit.
        exit 1
    fi
fi

Explanation

What I did was echoing a control sequences which searches for the regex isPhoneGap during an interactive reset process. Thus emulating a user who presses / to search for isPhoneGap, presses y when asked if he wants to discard this patch and finally presses q to exit the interactive reset.

The interactive reversed patch process is documented here: https://git-scm.com/docs/git-add#git-add-patch


NOTE: The above script assuming that the variable interactive.singleKey is false. If you configured yours to true, remove any $'\n' from the echo command right after the warning.

Currajong answered 5/9, 2017 at 19:30 Comment(0)
M
7

Gitx should let you commit or ignore individual lines (you may know that already), but you'd have to do that every time you commit. I think it would be better to have a config file per deployment target (you can version those), and some runtime parameter for however you are starting the server (like ./myserver --config=whatever.js).

Mcloughlin answered 2/7, 2011 at 14:12 Comment(0)
L
3

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.

Lyublin answered 26/4, 2013 at 20:36 Comment(0)
L
2

A content filter driver is not a good solution. You may be able to hide this line from git status/etc but it's not really being ignored. As soon as you change the value, your working directory will be marked dirty even though the change may not be visible.

If you really want this line out of version control, changing it to a command line argument or placing it in an ignored include or build file may be the only practical means.

Leucoma answered 28/5, 2014 at 7:54 Comment(2)
"As soon as you change the value, your working directory will be marked dirty even though the change may not be visible": it should not, if the clean script still restore the file in its original content.Chutzpah
That's what I had thought. I'm using GitExtensions and it will show the file as modified even tho the diff pane is blank. Possibly because clean filter isn't run until file check-in. Possibly because it's msysgit, not git-git. IDK, YMMVLeucoma
D
2

I think nobody has provided this way to work around the problem so, here's my technique. I keep logging/debugging/etc stuff in a single revision (actually, it could be multiple) in a private branch in my local repo. So, when I start a feature branch and I want to have all of my goodies (in my-goodies branch), I just do this:

git checkout my-feature
git cherry-pick my-feature..my-goodies # assuming both started from the same main branch, say, my-goodies started a few (or many) revisions back

Now I start working on the feature. When I am done, I rewrite history of the feature branch so that those revisions are gone:

git rebase -i @{u} # set all the goodies revisions to skip, save, and go

Now my branch is clean from all my goodies and I am fine to push anywhere.

Drear answered 7/6, 2021 at 22:57 Comment(0)
U
1

I'm guessing this might be something that comes up in more than one line of your source.

I think it would be cleanest to have a .userbuildconfig file of some sort that you just include and have the defaults checked in. Then you can use Carlos's suggestion to mark that file alone as assume unchanged. This way other changes to the file where you need to check the setting are not missed.

This can allow you to tune preprocessor macros locally (Or for java something like this https://mcmap.net/q/152868/-ifdef-ifndef-in-java).

Uvea answered 7/3, 2014 at 19:52 Comment(0)
L
0

Nope. You can only ignore individual files (and more) since the lines in .gitignore matches file names and not file content. You have already mentioned the solution to this, i.e. ignore a single file that contains that content you want to ignore.

Larousse answered 3/7, 2011 at 10:23 Comment(1)
I agree, in my case it was sufficient and possible to simply include that content from an ignored file. So moving all ignorable settings. there. Also, I created a copy of that ignored file and added it to the index as reference, so the information on what's to be in that file won't be lost.Ciceronian

© 2022 - 2024 — McMap. All rights reserved.