75

I'm looking for a way to 'hide' minor changes made to a few files in Git, such that they will not show up in git status until a different change is made to those files.

Example: I have a java file where the only change made is the removal of an unused import (a contributor forgot to run an organize imports before committing). Now I have removed that import and the change (obviously) shows up in git. Since I have no other change to make to that file, I don't really like committing the file as part of another (unrelated) change or committing this change stand-alone. Sure, I could revert the change and only applying it whenever I will have to make changes to that file, but I could "risk" forgetting it.

Does a command exists for such a task? It would work somewhat like the assume-unchanged command but in a not permanent way.

What would be the proper way to resolve this if no such command is available?

Thanks in advance.

Zelgadis
  • 906
  • 2
  • 10
  • 13
  • 1
    Why don't you simply create a "trivial fixes" branch for that? – Mat Nov 18 '12 at 16:43
  • @Mat what would be the gain from such a branch? I'd still have to merge it into master for the trivial changes to not show up. – Zelgadis Nov 18 '12 at 17:00
  • You'd keep all the trivial changes there, and merge them whenever it's ok in your dev cycle. That way they don't interfere with "proper" commits, but they're not forgotten either. – Mat Nov 18 '12 at 17:02
  • @Mat makes sense. I'd prefer something to hide trivial changes entirely but this could be a nice compromise, especially when the trivial changes starts to grow in number. Thanks – Zelgadis Nov 18 '12 at 17:07
  • 7
    Why wouldn't you make a commit with only that trivial change? This is not svn anymore, commit! – mgarciaisaia Nov 18 '12 at 17:19
  • @desert69 ultimately because it makes it harder to find the important changes in the log – Zelgadis Nov 18 '12 at 17:23
  • 1
    It doesn't seem very necessary in the first place. In my project it's considered a good practice to make commits minimal and to separate such cosmetic changes from significant functional changes. – Amir E. Aharoni Nov 18 '12 at 21:51
  • 1
    @Zelgadis: `git log | grep`, or even `git log -S""`. This question is still useful though, because sometimes there are changes that you don't want to commit, such as temporary edits for testing purposes. – naught101 Feb 05 '19 at 23:16

6 Answers6

115

In my use case (developing using an edited config file on my personal machine, running on another machine with the unchanged config), this was the solution for me:

start ignoring changes to a file:

git update-index --assume-unchanged path/to/file

keep tracking again:

git update-index --no-assume-unchanged path/to/file
benroth
  • 2,468
  • 3
  • 24
  • 25
  • 3
    Hah, thanks for the copy-paste example. I keep forgetting the exact syntax of these due to the infrequent use of it :p – Rob W Oct 25 '14 at 16:50
  • 1
    This is the best solution to this. Originally posted on 18 Feb 2009, Source: http://gitready.com/intermediate/2009/02/18/temporarily-ignoring-files.html – Kalpesh Panchal Apr 27 '17 at 17:45
  • Fantastic solution. – andromeda Nov 16 '18 at 07:41
  • 1
    Just a link to the [git docs](https://git-scm.com/docs/git-update-index#_using_assume_unchanged_bit), for those interested. – djvg Jan 16 '19 at 10:32
  • 11
    This is seems like it might cause paid when you forget that you've `--assume-unchanged` something, and you miss committing edits to that file. What would be really nice is if there was something similar that automatically reset itself whenever a file had an updated mtime or something like that. – naught101 Feb 05 '19 at 23:06
  • Is there a way to list the files that you've applied this to? – Harry Mar 01 '19 at 11:40
  • 1
    https://stackoverflow.com/questions/2363197/can-i-get-a-list-of-files-marked-assume-unchanged has a great [answer](https://stackoverflow.com/a/37083903/7153181) detailing how to get the list of files, and also to remove the flag from all files – Ryota Feb 21 '20 at 17:27
  • 1
    Thanks @Ryota. List ignored files tldr: `git ls-files -v | grep "^[a-z]"` – ford04 Apr 28 '20 at 13:05
  • note that using this method is **not recommended** by [git-scm](https://git-scm.com/docs/gitfaq#ignore-tracked-files). An ideal solution would be a scheduled gitignore. I have posted a question about the same [here](https://stackoverflow.com/q/76446451/6908282) – Gangula Jun 10 '23 at 15:01
15

The drawback of using git update-index --assume-unchanged is that (as the git manual states), you're promising git that you're not going to change the file.

When the "assume unchanged" bit is on, the user promises not to change the file and allows Git to assume that the working tree file matches what is recorded in the index.

If you violate this promise and change the file anyway it can have undesirable consequences. E.g. on some implementations, doing a git stash will throw away the changes you made to the file (why shouldn't it if you promised you weren't going to change it?).

Using the --skip-worktree option instead tells git to pretend the working version is up to date with the index version.

When reading an entry, if it is marked as skip-worktree, then Git pretends its working directory version is up to date and read the index version instead.

This seems to be the more reliable way to ignore changes without the risk that some git commands might throw your changes away:

git update-index --skip-worktree path/to/file

To track the file again:

git update-index --no-skip-worktree path/to/file

To list the files where you've set the skip-worktree:

git ls-files -v | grep ^S
HexAndBugs
  • 5,549
  • 2
  • 27
  • 36
  • This matches my use case best; I want to ignore local changes to the file, but still receive updates if upstream was modified. From https://compiledsuccessfully.dev/git-skip-worktree/ : "The file will be updated on a pull if the upstream file was changed." – Dawngerpony Dec 22 '21 at 09:52
5

Keep your changes that are not ready in a separate branch. git rebase this branch atop new changes in the main history as necessary.

Be it development in progress, temporary things, even things that are never to be included in the main project history -- the same process works equally well.

If/when the changes of the branch are ready to be included in the main history; merge it in. If not, keep them in the separate branch and continue rebasing.

(side note: git merge --no-ff may be of use to create a merge-commit even if a fast-forward merge is possible -- depending on the rules of your project, this may be preferable)

jsageryd
  • 4,234
  • 21
  • 34
5

Just don't add the trivial changes.

It's good practice to carefully review the things that you add before committing.

You can even ignore some changes in a file while adding others, using

git add -p.
Kraigolas
  • 5,121
  • 3
  • 12
  • 37
nes1983
  • 15,209
  • 4
  • 44
  • 64
1

I added all the answers here to add some code that made my life a little bit more comfortable. Expects BSD coreutils (like on MacOS).

git-overlook

#!/usr/bin/env bash

path="$1"
root="$(git rev-parse --show-toplevel)"

if [ -z "$path" ]; then
  echo "Specify a path" 1>&2
  exit 1;
fi;

if [ ! -d .git/overlook ]; then
    mkdir $root/.git/overlook
fi;
record="$root/.git/overlook/$(echo $path | sed 's/\./___/g' | sed 's/\//____/g')"
touch "$record"
git update-index --skip-worktree "$path"

git-relook

#!/usr/bin/env bash

path="$1"
root="$(git rev-parse --show-toplevel)"

record="$root/.git/overlook/$(echo $path | sed 's/\./___/g' | sed 's/\//____/g')"
if [ -f "$record" ]; then
  git update-index --no-skip-worktree "$path"
  rm "$record"
fi;

And finally a .git/hooks/pre-commit

#!/bin/bash

set -eu

root="$(git rev-parse --show-toplevel)"

get_record_name() {
  record="$root/.git/overlook/$(echo $1 | sed 's/\./___/g' | sed 's/\//____/g')"
  echo "$record"
}

for file in $(git ls-files -v | grep ^S | sed 's/S //'); do
  record="$(get_record_name $file)"
  record_mtime="$(stat -f %m $record)"
  file_mtime="$(stat -f %m $file)"

  if [ "$record_mtime" -lt "$file_mtime" ]; then
    echo "File $file is overlooked but has been modified since. Please run either git overlook $file or git relook $file" 1>&2
    exit 1
  else
    echo "File $file is overlooked but hasn't been modified"
  fi;

done;

This makes it so that I can run git overlook path/to/file and then I can go about committing on my merry way until I've changed the file that was overlooked, at which point I have to either overlook it again or bring it back in with git relook path/to/file. Obviously it doesn't help me if the overlooked file is the only change I made and then I don't see it in git status.

It makes some files in .git/overlook to keep track of stuff but as far as I know git will peacefully coexist with them.

Roshan
  • 153
  • 9
0

There are multiple ways [although may not be clean and neat and would require your attention]

  1. Add the file in concern to .gitignore in your repo so that it doesn't show up for commit. Be careful to remove this from .gitignore when you are ready to commit the file
  2. Ensure you do not 'stage' the file while committing rest of your changes. You may want to write a wrapper over git which will ensure commands like git commit -a or git add . run on all except the file under question. Another alternative would be to use git gui or git citool where you can visually ensure your file isn't in 'staged' area and hence never gets committed
  3. Another way would be to commit all your 'committable' changes and then git stash save your only working file. Later when you are ready to change the file, you can git stash pop and continue working and committing.

Hope that helps :)

Anshul
  • 680
  • 1
  • 5
  • 19
  • 1
    The first solution unfortunately doesn't work, since the files are already tracked. A mix of 2 and 3 is what I am using right now, but I'd like something more automatic. Thanks for the answer anyway! – Zelgadis Nov 18 '12 at 17:01
  • Unfortunately, `git` can't yet read the developer's mind to figure out if a change is important (and should be commited) or not (keep it out). The cleanest options are (a) separate branch for "trivial fixes", rebased regularly and merged in at some points; (b) mark changes as "trivial" in the commit message. IMHO (b) is preferable, as it avoids skews and versions that nobody ever tested. – vonbrand Feb 11 '13 at 19:39