-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Added edit/remove functionality to git-ignore #1258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
c1cf87f
0546411
69124b9
044f8e6
3e5076b
91bf8db
99815fa
33acecb
dda01a3
eb28e6e
9e21478
a9d9d3b
33b43dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) | ||
|
|
||
|
|
||
| show_contents() { | ||
| local file="${2/#~/$HOME}" | ||
| if [ -f "$file" ]; then | ||
|
|
@@ -29,6 +30,17 @@ cd_to_git_root() { | |
| fi | ||
| } | ||
|
|
||
| get_tmp_file() { | ||
| local tmpfile= | ||
| if ! git rev-parse --git-dir &>/dev/null || [ -z "$GIT_DIR" ]; then | ||
| tmpfile="$(mktemp -q "${TMPDIR:-/tmp}"/git-extras-ignore.XXXXXX 2>/dev/null)" | ||
| else | ||
| cd_to_git_root --warn &>/dev/null | ||
| tmpfile="$(mktemp -q "$GIT_DIR"/git-extras-ignore.XXXXXX 2>/dev/null)" | ||
| fi | ||
| echo "${tmpfile:-$(mktemp -q)}" | ||
| } | ||
|
|
||
| global_ignore() { | ||
| if ! git config --global core.excludesFile 2>/dev/null; then | ||
| if [ -f "$HOME/.gitignore" ]; then | ||
|
|
@@ -94,29 +106,157 @@ add_patterns() { | |
| done | ||
| } | ||
|
|
||
| # TODO: create a file if does not exist?? | ||
| edit_file() { | ||
| local file="${2/#~/$HOME}" | ||
| if [ -f "$file" ]; then | ||
| echo "Editing $1 gitignore ($2)..." && $(git var GIT_EDITOR) "$file" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. $(git var GIT_EDITOR) "$file" loses shell quoting for editor values like vim -c 'set ft=gitignore', and echo "Done." still runs even if the editor exits non-zero.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed the line to use eval - it seems to fix this issue. |
||
| echo "Done." | ||
| else | ||
| echo "There is no $1 .gitignore yet." >&2 | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| _usage() { | ||
| # TODO | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's either fulfill it or drop it |
||
| echo "Under construction." | ||
| } | ||
|
|
||
| remove_patterns() { | ||
| declare -a args | ||
| local tmpfile | ||
| args=( "${@:3}" ) | ||
| tmpfile="$(get_tmp_file)" | ||
| local file="${2/#~/$HOME}" | ||
| if [ -f "$file" ]; then | ||
| echo "Removing patterns from $1 gitignore ($2)..." | ||
| while read -r line; do | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can corrupt preserved lines during remove. read without IFS= trims leading/trailing whitespace, echo "$line" mishandles values like -n, and the loop skips a final line without a trailing newline. Use while IFS= read -r line || [ -n "$line" ]; do and printf '%s\n' "$line".
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While thinking about these issues found that perl has \E \Q escapes. These could work too (something like perl -pe "s/\Q${pattern1}\E\n?//; s/\Q${pattern2}\E\n?//; ..."). Which of the approaches do you think is better?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we have to add perl in https://github.com/tj/git-extras/blob/main/Installation.md#dependencies. Could it be solved with awk or sed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I found a way to do that in awk and also keep echos saying what was dropped. Added it in 3e5076b. |
||
| local drop= | ||
| for pattern in "${args[@]}"; do | ||
| test "$pattern" = "$line" && drop="yes" | ||
| done | ||
| if [ -z "$drop" ]; then | ||
| echo "$line" >> "$tmpfile" | ||
| else | ||
| echo "Removed $line pattern." | ||
| fi | ||
| done < "$file" | ||
| mv "$tmpfile" "$file" # tmp file created in get_tmp_file 'deleted' | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If .gitignore or the global ignore file is a symlink, mv "$tmpfile" "$file" replaces the symlink instead of updating its target; it can also lose file metadata. Can we update it in place? |
||
| echo "Done." | ||
| else | ||
| echo "There is no $1 .gitignore yet" | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
|
|
||
| # can have only 1 action and 1 level, if conflicting flags are passed | ||
| # latter ones overwrite former | ||
| # sanity of args not tested (e.g. there should be no args if -e is supplied) | ||
| if test $# -eq 0; then | ||
| show_global | ||
| echo "---------------------------------" | ||
| show_local | ||
| echo "---------------------------------" | ||
| show_private | ||
| else | ||
| case "$1" in | ||
| -l|--local) | ||
| test $# -gt 1 && add_local "${@:2}" && echo | ||
| show_local | ||
| ;; | ||
| -g|--global) | ||
| test $# -gt 1 && add_global "${@:2}" && echo | ||
| show_global | ||
| ;; | ||
| -p|--private) | ||
| test $# -gt 1 && add_private "${@:2}" && echo | ||
| show_private | ||
| ;; | ||
| *) | ||
| add_local "$@" | ||
| ;; | ||
| esac | ||
| fi | ||
| # parse flags and args | ||
| level="none" | ||
| action="none" | ||
| declare -a args | ||
| while [ "$1" != "" ]; do | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It breaks documented empty-string patterns. while [ "$1" != "" ] stops parsing as soon as a pattern is "", so existing examples like git ignore --local "" "# Temporary files" no longer add anything after the empty argument. Use while [ $# -gt 0 ] instead. |
||
| case "$1" in | ||
| -h|--help) | ||
| _usage | ||
| exit 0 | ||
| ;; | ||
| -l|--local) | ||
| level="local" | ||
| ;; | ||
| -g|--global) | ||
| level="global" | ||
| ;; | ||
| -p|--private) | ||
| level="private" | ||
| ;; | ||
| -e|--edit) | ||
| action="edit" | ||
| ;; | ||
| -r|--remove) | ||
| action="remove" | ||
| ;; | ||
| --) | ||
| shift | ||
| args+=("$@") | ||
| break | ||
| ;; | ||
| *) | ||
| args+=("$1") | ||
| ;; | ||
| esac | ||
| shift | ||
| done | ||
| # compatible with previous version | ||
| if [ "$action" = "none" ]; then | ||
| case "$level" in | ||
| local) | ||
| test ${#args[@]} -ne 0 && add_local "${args[@]}" && echo | ||
| show_local | ||
| ;; | ||
| global) | ||
| test ${#args[@]} -ne 0 && add_global "${args[@]}" && echo | ||
| show_global | ||
| ;; | ||
| private) | ||
| test ${#args[@]} -ne 0 && add_private "${args[@]}" && echo | ||
| show_private | ||
| ;; | ||
| none) | ||
| add_local "${args[@]}" | ||
| # maybe show_local here too? | ||
| ;; | ||
| esac | ||
| exit 0 | ||
| # open file in editor | ||
| elif [ "$action" = "edit" ]; then | ||
| case "$level" in | ||
| local|none) | ||
| cd_to_git_root --warn | ||
| edit_file 'local' .gitignore | ||
| ;; | ||
| global) | ||
| global_gitignore="$(global_ignore)" | ||
| edit_file global "$global_gitignore" | ||
| ;; | ||
| private) | ||
| cd_to_git_root --error | ||
| test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info" | ||
| edit_file private "${GIT_DIR}/info/exclude" | ||
| ;; | ||
| esac | ||
| exit 0 | ||
| # remove entries | ||
| else | ||
| if [ ${#args[@]} -eq 0 ]; then | ||
| echo "Nothing to remove." | ||
| exit 0 | ||
| fi | ||
| case "$level" in | ||
| local|none) | ||
| cd_to_git_root --warn | ||
| remove_patterns 'local' .gitignore "${args[@]}" | ||
| ;; | ||
| global) | ||
| global_gitignore="$(global_ignore)" | ||
| remove_patterns global "$global_gitignore" "${args[@]}" | ||
| ;; | ||
| private) | ||
| cd_to_git_root --error | ||
| remove_patterns private "${GIT_DIR}/info/exclude" "${args[@]}" | ||
| ;; | ||
| esac | ||
| exit 0 | ||
|
|
||
| fi | ||
| fi | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we always put the temp file in $TMPDIR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wanted it to work the same way git does and it writes its tmp files in .git. Changed to always use global tmp directory.