git

Gitignore: Excluding Everything Except a Specific Directory

I have the following directory structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ tree -aF src/
src/
├── github.com/
│   ├── git/
│   │   └── git/
│   │   └── file
│   └── realguess/
│   ├── git/
│   │   └── file
│   └── hello/
│   └── hello.go
└── .gitignore
6 directories, 4 files

My intention is to keep the projects in src/github.com/realguess/ only. So, I have added the following gitignore file:

1
2
3
$ cat src/.gitignore
/github.com/
!/github.com/realguess/

But the directory is ignored:

1
2
$ git check-ignore -v github.com/realguess/
src/.gitignore:1:/github.com/ github.com/realguess/

It is not possible to re-include a file if a parent directory of that file is excluded. Git doesn’t list excluded directories for performance reasons, so any patterns on contained files have no effect, no matter where they are defined.[^1]

Uh-oh! The directory github.com/ is excluded. Therefore, it is not possible to re-include a file github.com/realguess (directory is a file). To fix it, just need to add the wildcard to the end of the parent directory:

1
2
3
$ cat src/.gitignore
/github.com/*
!/github.com/realguess/

By adding the wildcard character, it matches every file (including hidden ones) or directory under github.com/ directory, the directory itself as the parent of github.com/realguess/ is not excluded. Then, we use the negation pattern to re-include github.com/realguess/.

Double check it:

1
2
$ git check-ignore -v github.com/realguess/
1

The debugging command returns non-zero status code, which indicates that the directory is not ignored.

Listing Tags in Natural Sort of Version Numbers

Using the Node.js repository as an example:

1
2
3
$ git remote -v
origin https://github.com/nodejs/node.git (fetch)
origin https://github.com/nodejs/node.git (push)

If we would like to list all tags with v0.12 versions, we could do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git tag -l 'v0.12.*'
v0.12.0
v0.12.1
v0.12.10
v0.12.11
v0.12.12
v0.12.13
v0.12.14
v0.12.15
v0.12.2
v0.12.3
v0.12.4
v0.12.5
v0.12.6
v0.12.7
v0.12.8
v0.12.8-rc.1
v0.12.9

However, v0.12.2 should come after v0.12.1.

To fix it, we use the sort command with option:

1
2
-V, --version-sort
natural sort of (version) numbers within text

Thus:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git tag -l 'v0.12.*' | sort --version-sort
v0.12.0
v0.12.1
v0.12.2
v0.12.3
v0.12.4
v0.12.5
v0.12.6
v0.12.7
v0.12.8
v0.12.8-rc.1
v0.12.9
v0.12.10
v0.12.11
v0.12.12
v0.12.13
v0.12.14
v0.12.15

Git End-of-Line Normalization by AutoCRLF with Input

Using Vagrant file provision in a Windows host, the line endings are not converted from CRLF to LF (Linux systems). So, code cannot be executed properly in the guest Linux system. And also this is a frequently occurred message for working with Windows:

1
2
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.

One solution is to use UNIX line ending (LF) even in Windows, hence no conversion is needed. This is done by setting:

1
$ git config --global core.autocrlf input

Which means when committing the code to a remote repository, the line ending will be convert from CRLF to LF if any, but when checking out from the remote directory, no conversion from LF to CRLF is performed.