Skip to content

Commit 145963b

Browse files
committed
Change to writing into the gitignore hidden inside the .git directory
This makes ignoring work automagically for people, while minimizing the code changes they have to think about or check in. #100 and #59 are examples of use cases that this simplifies. It also marginally simplifies the case where people can't commit use of this tool to the repo they're working on. IMO tools should to do this more broadly, especially now that git is so dominant. Hidden gitignore documented in https://git-scm.com/docs/gitignore
1 parent adf45c4 commit 145963b

File tree

2 files changed

+25
-24
lines changed

2 files changed

+25
-24
lines changed

.gitignore

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
# Ignore macOS folder attributes.
22
.DS_Store
33

4-
### This tool (`bazel-compile-commands-extractor`) automatically ensures the following entries are present in any WORKSPACE from which it is invoked:
5-
# Ignore the `external` link (that is added by `bazel-compile-commands-extractor`). The link differs between macOS/Linux and Windows, so it shouldn't be checked in. The pattern must not end with a trailing `/` because it's a symlink on macOS/Linux.
6-
/external
74
# Ignore links to Bazel's output. The pattern needs the `*` because people can change the name of the directory into which the repository is cloned (changing the `bazel-<workspace_name>` symlink), and must not end with a trailing `/` because it's a symlink on macOS/Linux.
85
/bazel-*
9-
# Ignore generated output. Although valuable (after all, the primary purpose of `bazel-compile-commands-extractor` is to produce `compile_commands.json`!), it should not be checked in.
10-
/compile_commands.json
11-
# Ignore the directory in which `clangd` stores its local index.
12-
/.cache/

refresh.template.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -972,28 +972,36 @@ def _ensure_external_workspaces_link_exists():
972972

973973

974974
def _ensure_gitignore_entries_exist():
975-
"""Ensure `/compile_commands.json`, `/external`, and other useful entries are `.gitignore`'d if it looks like git is used."""
976-
977-
# Do nothing if we aren't within a git repository and there is no `.gitignore` file. We still add to the .gitignore file if it exists, even if we aren't in a git repository (perhaps because git was temporarily uninstalled).
978-
if not os.path.isfile('.gitignore'): # Check .gitignore first as a fast path
979-
# Silently check if we're (nested) within a git repository. It isn't sufficient to check for the presence of a `.git` directory, in case the bazel workspace lives underneath the top-level git repository.
980-
# See https://stackoverflow.com/questions/2180270/check-if-current-directory-is-a-git-repository for a few ways to test.
981-
is_git_repository = subprocess.run('git rev-parse --git-dir',
982-
shell=True, # Ensure this will still fail with a nonzero error code even if `git` isn't installed, unifying error cases.
983-
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
984-
).returncode == 0 # A nonzero error code indicates that we are not (nested) within a git repository.
985-
if not is_git_repository: return
975+
"""Ensure `//compile_commands.json`, `//external`, and other useful entries are `.gitignore`'d if in a git repo."""
976+
# Silently check if we're (nested) within a git repository. It isn't sufficient to check for the presence of a `.git` directory, in case the bazel workspace is nested inside the git repository.
977+
git_dir_process = subprocess.run('git rev-parse --git-dir',
978+
shell=True, # Ensure this will still fail with a nonzero error code even if `git` isn't installed, unifying error cases.
979+
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
980+
encoding=locale.getpreferredencoding(),
981+
)
982+
# A nonzero error code indicates that we are not (nested) within a git repository.
983+
if git_dir_process.returncode: return
984+
985+
# Write into the gitignore hidden inside the .git directory
986+
# This makes ignoring work automagically for people, while minimizing the code changes they have to think about or check in. https://github.com/hedronvision/bazel-compile-commands-extractor/pull/100 and https://github.com/hedronvision/bazel-compile-commands-extractor/issues/59 are exampels of use cases that this simplifies. It also marginally simplifies the case where people can't commit use of this tool to the repo they're working on.
987+
# IMO tools should to do this more broadly, especially now that git is so dominant.
988+
# Hidden gitignore documented in https://git-scm.com/docs/gitignore
989+
git_dir = pathlib.Path(git_dir_process.stdout.rstrip())
990+
hidden_gitignore_path = git_dir / 'info' / 'exclude'
991+
pattern_prefix = str(pathlib.Path.cwd().relative_to(git_dir.parent.absolute()))
992+
if pattern_prefix == '.': pattern_prefix = ''
993+
elif pattern_prefix: pattern_prefix += '/'
986994

987995
# Each (pattern, explanation) will be added to the `.gitignore` file if the pattern isn't present.
988996
needed_entries = [
989-
('/external', "# Ignore the `external` link (that is added by `bazel-compile-commands-extractor`). The link differs between macOS/Linux and Windows, so it shouldn't be checked in. The pattern must not end with a trailing `/` because it's a symlink on macOS/Linux."),
990-
('/bazel-*', "# Ignore links to Bazel's output. The pattern needs the `*` because people can change the name of the directory into which your repository is cloned (changing the `bazel-<workspace_name>` symlink), and must not end with a trailing `/` because it's a symlink on macOS/Linux."),
991-
('/compile_commands.json', "# Ignore generated output. Although valuable (after all, the primary purpose of `bazel-compile-commands-extractor` is to produce `compile_commands.json`!), it should not be checked in."),
992-
('/.cache/', "# Ignore the directory in which `clangd` stores its local index."),
997+
(f'/{pattern_prefix}external', "# Ignore the `external` link (that is added by `bazel-compile-commands-extractor`). The link differs between macOS/Linux and Windows, so it shouldn't be checked in. The pattern must not end with a trailing `/` because it's a symlink on macOS/Linux."),
998+
(f'/{pattern_prefix}bazel-*', "# Ignore links to Bazel's output. The pattern needs the `*` because people can change the name of the directory into which your repository is cloned (changing the `bazel-<workspace_name>` symlink), and must not end with a trailing `/` because it's a symlink on macOS/Linux. This ignore pattern should almost certainly be checked into a .gitignore in your workspace root, too, for folks who don't use this tool."),
999+
(f'/{pattern_prefix}compile_commands.json', "# Ignore generated output. Although valuable (after all, the primary purpose of `bazel-compile-commands-extractor` is to produce `compile_commands.json`!), it should not be checked in."),
1000+
(f'/{pattern_prefix}.cache/', "# Ignore the directory in which `clangd` stores its local index."),
9931001
]
9941002

9951003
# Create `.gitignore` if it doesn't exist (and don't truncate if it does) and open it for appending/updating.
996-
with open('.gitignore', 'a+') as gitignore:
1004+
with open(hidden_gitignore_path, 'a+') as gitignore:
9971005
gitignore.seek(0) # Files opened in `a` mode seek to the end, so we reset to the beginning so we can read.
9981006
# Recall that trailing spaces, when escaped with `\`, are meaningful to git. However, none of the entries for which we're searching end with literal spaces, so we can safely trim all trailing whitespace. That said, we can't rewrite these stripped lines to the file, in case an existing entry is e.g. `/foo\ `, matching the file "foo " (with a trailing space), whereas the entry `/foo\` does not match the file `"foo "`.
9991007
lines = [l.rstrip() for l in gitignore]
@@ -1010,7 +1018,7 @@ def _ensure_gitignore_entries_exist():
10101018
for pattern, comment in missing:
10111019
print(comment, file=gitignore)
10121020
print(pattern, file=gitignore)
1013-
log_success(">>> Automatically added entries to .gitignore to avoid problems.")
1021+
log_success(">>> Automatically added entries to .git/info/exclude to gitignore generated output.")
10141022

10151023

10161024
def _ensure_cwd_is_workspace_root():

0 commit comments

Comments
 (0)