diff --git a/bin/git-ignore b/bin/git-ignore index 34a17913..f7c2ab55 100755 --- a/bin/git-ignore +++ b/bin/git-ignore @@ -2,6 +2,26 @@ GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) +# awk script that takes 2 input files. The first one is newline deilmeted +# string of patterns to look for, second one is the file to remove them from. +# outfile has to be passed with -v upon call and is the file result is +# written to. +read -r -d '' AWK_SCRIPT <<'EOF' +# process the first "file" (newline split argument list) +NR==FNR { a[b++]=$0; next; } +# process file +{ + found=0; + for (i=0; i != b; i++) { + if (a[i] == $0) { found=1; } + } + if (found == 1) { print "... removing '"$0"'"; } + else { print $0 > outfile } +} +EOF + + + show_contents() { local file="${2/#~/$HOME}" if [ -f "$file" ]; then @@ -94,6 +114,66 @@ add_patterns() { done } +edit_file() { + local file="${2/#~/$HOME}" + if [ -f "$file" ]; then + echo "Editing $1 gitignore: ($2)" && eval "$(git var GIT_EDITOR) $(printf '%q' "$file")" + echo "Done." + else + echo "There is no $1 .gitignore yet." >&2 + exit 1 + fi +} + +_usage() { + cat << EOF +Usage: git ignore [OPTION]... [PATTERN]... +Modify or display local global or private gitignore files. + +OPTIONs set context (local, global, private) and action (display, add, +remove, edit). In case of collision, last flag overwrites preceding ones. +Defaults are: local context, action is display (see man git ignore for +more details). +Context examples: +local: .../repo/.gitignore +private: .../repo/.git/info/exclude +global: \$HOME/.gitignore + +PATTERNs are the gitignore patterns as specified in git documentation. + +Options: + -l, --local Set context to local gitignore + -g, --global Set context to global gitignore + -p, --private Set context to private gitignore + -e, --edit Set action to edit + -r, --remove Set action to remove + -h, --help Print this message + -- End of options delimiter +EOF +} + +remove_patterns() { + declare -a args + local tmpfile + args=( "${@:3}" ) + local file="${2/#~/$HOME}" + if [ -f "$file" ]; then + tmpfile="$(mktemp -q "${TMPDIR:-/tmp}"/git-extras-ignore.XXXXXX 2>/dev/null)" + echo "Removing patterns from $1 gitignore: $2" + awk -v outfile="$tmpfile" "${AWK_SCRIPT}"\ + <(printf '%s\n' "${args[@]}") "$file" + cat "$tmpfile" > "$file" + rm "$tmpfile" + 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 "---------------------------------" @@ -101,22 +181,105 @@ if test $# -eq 0; then 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 [ $# -gt 0 ]; do + 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 && echo + ;; + global) + global_gitignore="$(global_ignore)" + edit_file global "$global_gitignore" && echo + ;; + private) + cd_to_git_root --error + test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info" + edit_file private "${GIT_DIR}/info/exclude" && echo + ;; + 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[@]}" && echo + show_local + ;; + global) + global_gitignore="$(global_ignore)" + remove_patterns global "$global_gitignore" "${args[@]}" && echo + show_global + ;; + private) + cd_to_git_root --error + remove_patterns private "${GIT_DIR}/info/exclude" "${args[@]}" && echo + show_private + ;; + esac + exit 0 + fi +fi diff --git a/etc/bash_completion.sh b/etc/bash_completion.sh index d48cdb34..06fd02ef 100644 --- a/etc/bash_completion.sh +++ b/etc/bash_completion.sh @@ -145,11 +145,11 @@ _git_graft(){ _git_ignore(){ case "$cur" in --*) - __gitcomp "--global --local --private" + __gitcomp "--global --local --private --edit --remove" return ;; -*) - __gitcomp "--global --local --private -g -l -p" + __gitcomp "--global --local --private --edit --remove -g -l -p -e -r" return ;; esac diff --git a/etc/git-extras-completion.zsh b/etc/git-extras-completion.zsh index 9b95c585..c6aab46e 100644 --- a/etc/git-extras-completion.zsh +++ b/etc/git-extras-completion.zsh @@ -317,9 +317,11 @@ _git-guilt() { _git-ignore() { _arguments -C \ - '(--local -l)'{--local,-l}'[show local gitignore]' \ - '(--global -g)'{--global,-g}'[show global gitignore]' \ - '(--private -p)'{--private,-p}'[show repo gitignore]' \ + '(--local -l)'{--local,-l}'[set context to local gitignore]' \ + '(--global -g)'{--global,-g}'[set context to global gitignore]' \ + '(--private -p)'{--private,-p}'[set context to private gitignore]' \ + '(--edit -e)'{--edit,-e}'[set action to edit]' \ + '(--remove -r)'{--remove,-r}'[set action to remove]' \ '*:filename:_files' } @@ -443,7 +445,7 @@ zstyle ':completion:*:*:git:*' user-commands $existing_user_commands \ graft:'merge and destroy a given branch' \ guilt:'calculate change between two revisions' \ ignore-io:'get sample gitignore file' \ - ignore:'add .gitignore patterns' \ + ignore:'Modify or display .gitignore files' \ info:'returns information on current repository' \ local-commits:'list local commits' \ lock:'lock a file excluded from version control' \ diff --git a/etc/git-extras.fish b/etc/git-extras.fish index 976cfca0..5104b277 100644 --- a/etc/git-extras.fish +++ b/etc/git-extras.fish @@ -32,7 +32,7 @@ set __fish_git_extras_commands \ "graft:Merge and destroy a given branch" \ "guilt:calculate change between two revisions" \ "ignore-io:Get sample gitignore file" \ - "ignore:Add .gitignore patterns" \ + "ignore:Modify or display .gitignore files" \ "info:Returns information on current repository" \ "local-commits:List local commits" \ "lock:Lock a file excluded from version control" \ @@ -162,9 +162,11 @@ complete -c git -f -n '__fish_git_using_command guilt' -s e -l email -d 'display complete -c git -f -n '__fish_git_using_command guilt' -s d -l debug -d 'output debug information' complete -c git -f -n '__fish_git_using_command guilt' -s h -d 'output usage information' # ignore -complete -c git -f -n '__fish_git_using_command ignore' -s l -l local -d 'show local gitignore' -complete -c git -f -n '__fish_git_using_command ignore' -s g -l global -d 'show global gitignore' -complete -c git -f -n '__fish_git_using_command ignore' -s p -l private -d 'show repo gitignore' +complete -c git -f -n '__fish_git_using_command ignore' -s l -l local -d 'set context to local gitignore' +complete -c git -f -n '__fish_git_using_command ignore' -s g -l global -d 'set context to global gitignore' +complete -c git -f -n '__fish_git_using_command ignore' -s p -l private -d 'set context to private gitignore' +complete -c git -f -n '__fish_git_using_command ignore' -s e -l edit -d 'set action to edit' +complete -c git -f -n '__fish_git_using_command ignore' -s r -l remove -d 'set action to remove' # ignore-io function __fish_git_extra_get_ignore_io_types # we will first remove every tab spaces, and then append `\t` at the end to remove the default description diff --git a/man/git-extras.md b/man/git-extras.md index 2dec9314..178595ba 100644 --- a/man/git-extras.md +++ b/man/git-extras.md @@ -62,7 +62,7 @@ git-extras(1) -- Awesome GIT utilities - **git-graft(1)** Merge and destroy a given branch - **git-guilt(1)** calculate change between two revisions - **git-ignore-io(1)** Get sample gitignore file - - **git-ignore(1)** Add .gitignore patterns + - **git-ignore(1)** Modify or display .gitignore files - **git-info(1)** Returns information on current repository - **git-local-commits(1)** List local commits - **git-lock(1)** Lock a file excluded from version control diff --git a/man/git-ignore.1 b/man/git-ignore.1 index 088871c4..1ec5efa9 100644 --- a/man/git-ignore.1 +++ b/man/git-ignore.1 @@ -1,77 +1,88 @@ -.\" generated with Ronn/v0.7.3 -.\" http://github.com/rtomayko/ronn/tree/0.7.3 -. -.TH "GIT\-IGNORE" "1" "April 2018" "" "Git Extras" -. +.\" generated with Ronn-NG/v0.10.1 +.\" http://github.com/apjanke/ronn-ng/tree/0.10.1 +.TH "GIT\-IGNORE" "1" "June 2026" "" "Git Extras" .SH "NAME" -\fBgit\-ignore\fR \- Add \.gitignore patterns -. +\fBgit\-ignore\fR \- Modify or display \.gitignore files .SH "SYNOPSIS" -\fBgit\-ignore\fR [] [ []\.\.\.] -. +\fBgit\-ignore\fR [] [] [\-\-] [ []\|\.\|\.\|\.] .SH "DESCRIPTION" -Adds the given _pattern_s to a \.gitignore file if it doesn\'t already exist\. -. +Modifies or shows \.gitignore of selected \fIcontext\fR\. +.P +Possible actions are: addition of new \fIpattern\fRs, removal of existing \fIpattern\fRs from gitignore files, opening these files in text editor or simply displaying their contents in current terminal (for details see flags/usage)\. +.P +Contexts are global (e\.g\. $HOME/\.gitignore or $HOME/\.local/\.git/\.gitignore), private (e\.g\. \|\.\|\.\|\./project/\.git/info/exclude) and local (e\.g\. \|\.\|\.\|\./project/\.gitignore) +.P +Adding only happens for \fIpattern\fRs that don't already exist in the file; removing \fIpattern\fRs that are not present does not do anything; editing fails if the file is not yet present\. +.P +At most 1 \fIcontext\fR and 1 \fIaction\fR matter for the program, the ones selected are the latest encountered in the flag list\. +.P +Default behavior: +.IP "\(bu" 4 +If no flags or arguments are passed, prints gitignores of all 3 \fIcontext\fRs\. +.IP "\(bu" 4 +Passing arguments with no \fIaction\fR implies addition of \fIpattern\fRs to gitignore\. +.IP "\(bu" 4 +If an \fIaction\fR is chosen, but not \fIcontext\fR, \fIaction\fR is performed on local gitignore\. +.IP "\(bu" 4 +If \fIcontext\fR is chosen but not action (not even implied), gitignore of chosen \fIcontext\fR is printed\. +.IP "" 0 .SH "OPTIONS" -. .P \-l, \-\-local -. .P Sets the context to the \.gitignore file in the current working directory\. (default) -. .P \-g, \-\-global -. .P Sets the context to the global gitignore file for the current user\. -. .P \-p, \-\-private -. .P Sets the context to the private exclude file for the repository (\fB\.git/info/exclude\fR)\. -. +.P + +.P +\-e, \-\-edit +.P +Sets action to edit\. This will open gitignore in a text editor (editor selection is the same as for git commit)\. If no \fIcontext\fR is provided local is assumed\. Any \fIpattern\fR; provided in additional arguments is silently omitted\. +.P +\-r, \-\-remove +.P +Sets action to remove\. This will look for \fIpattern\fRs in gitignore of selected \fIcontext\fR and remove if finds them\. If no \fIpattern\fR is provided does nothing\. All characters in \fIpattern\fR are treated literally, no interpretation of metacharacters is performed as would be, for example, for a regex pattern\. .P -. .P A space delimited list of patterns to append to the file in context\. -. +.P +\-\- +.P +End of options delimeter\. Everything to the right of it is treated as positional arguments or more precisely a \fIpattern\fR\. +.P +\-h, \-\-help +.P +Print a brief usage summary\. .SS "PATTERN FORMAT" Pattern format as described in the git manual -. .IP "\(bu" 4 A blank line matches no files, so it can serve as a separator for readability\. To append a blank line use empty quotes ""\. -. .IP "\(bu" 4 A line starting with # serves as a comment\. For example, "# This is a comment" -. .IP "\(bu" 4 -An optional prefix ! which negates the pattern; any matching file excluded by a previous pattern will become included again\. If a negated pattern matches, this will override lower precedence patterns sources\. To use an exclamation ! as command line argument it is best placed between single quotes \'\'\. For example, \'!src\' -. +An optional prefix ! which negates the pattern; any matching file excluded by a previous pattern will become included again\. If a negated pattern matches, this will override lower precedence patterns sources\. To use an exclamation ! as command line argument it is best placed between single quotes ''\. For example, '!src' .IP "\(bu" 4 If the pattern ends with a slash, it is removed for the purpose of the following description, but it would only find a match with a directory\. In other words, foo/ will match a directory foo and paths underneath it, but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in git)\. -. .IP "\(bu" 4 If the pattern does not contain a slash /, git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the \.gitignore file (relative to the top level of the work tree if not from a \.gitignore file)\. -. .IP "\(bu" 4 Otherwise, git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname\. For example, "Documentation/*\.html" matches "Documentation/git\.html" but not "Documentation/ppc/ppc\.html" or "tools/perf/Documentation/perf\.html"\. -. .IP "\(bu" 4 A leading slash matches the beginning of the pathname\. For example, "/*\.c" matches "cat\-file\.c" but not "mozilla\-sha1/sha1\.c"\. -. .IP "" 0 -. .SH "EXAMPLES" -All arguments are optional so calling git\-ignore alone will display first the global then the local gitignore files: -. +All arguments are optional so calling git\-ignore alone will display global, local and private gitignore files in order: .IP "" 4 -. .nf - $ git ignore Global gitignore: /home/alice/\.gitignore # Numerous always\-ignore extensions @@ -86,92 +97,101 @@ Global gitignore: /home/alice/\.gitignore *\.sass\-cache # OS or Editor folders -\.DS_Store -\.Trashes -\._* +\&\.DS_Store +\&\.Trashes +\&\._* Thumbs\.db \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- Local gitignore: \.gitignore -\.cache -\.project -\.settings -\.tmproj +\&\.cache +\&\.project +\&\.settings +\&\.tmproj nbproject -. +\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- +Private gitignore: \.git/info/exclude +notes\.txt +test\.sh +\&\.env\.local +config\.json .fi -. .IP "" 0 -. .P -If you only want to see the global context use the \-\-global argument (for local use \-\-local): -. +If you only want to see the global context use the \-\-global argument (for local use \-\-local, for private use \-\-private): .IP "" 4 -. .nf - $ git ignore Global gitignore: /home/alice/\.gitignore -\.DS_Store -\.Trashes -\._* +\&\.DS_Store +\&\.Trashes +\&\._* Thumbs\.db -. .fi -. .IP "" 0 -. .P To quickly append a new pattern to the default/local context simply: -. .IP "" 4 -. .nf - $ git ignore *\.log Adding pattern(s) to: \.gitignore -\.\.\. adding \'*\.log\' -. +\|\.\|\.\|\. adding '*\.log' .fi -. .IP "" 0 -. .P -You can now configure any patterns without ever using an editor, with a context and pattern arguments: The resulting configuration is also returned for your convenience\. -. +You can now configure any patterns without ever using an editor (or use a shortcut to open file in editor when needed), with a context and pattern arguments: The resulting configuration is also returned for your convenience\. .IP "" 4 -. .nf - -$ git ignore \-\-local "" "# Temporary files" *\.tmp "*\.log" tmp/* "" "# Files I\'d like to keep" \'!work\' "" +$ git ignore \-\-local "" "# Temporary files" *\.tmp "*\.log" tmp/* "" "# Files I'd like to keep" '!work' "" Adding pattern(s) to: \.gitignore -\.\.\. adding \'\' -\.\.\. adding \'# Temporary files\' -\.\.\. adding \'index\.tmp\' -\.\.\. adding \'*\.log\' -\.\.\. adding \'tmp/*\' -\.\.\. adding \'\' -\.\.\. adding \'# Files I\'d like to keep\' -\.\.\. adding \'!work\' -\.\.\. adding \'\' +\|\.\|\.\|\. adding '' +\|\.\|\.\|\. adding '# Temporary files' +\|\.\|\.\|\. adding '*\.tmp' +\|\.\|\.\|\. adding '*\.log' +\|\.\|\.\|\. adding 'tmp/*' +\|\.\|\.\|\. adding '' +\|\.\|\.\|\. adding '# Files I'd like to keep' +\|\.\|\.\|\. adding '!work' +\|\.\|\.\|\. adding '' Local gitignore: \.gitignore # Temporary files -index\.tmp +*\.tmp *\.log +tmp/* + +# Files I'd like to keep +!work +.fi +.IP "" 0 +.P +Removing patterns works in a similar way\. Select context and pass patterns to be removed from the gitignore file\. +.IP "" 4 +.nf +$ git ignore \-l \-r "*\.log" +Removing patterns from local gitignore: \.gitignore +\|\.\|\.\|\. removing '*\.log' + +Local gitignore: \.gitignore + +# Temporary files +*\.tmp +tmp/* -# Files I\'d like to keep +# Files I'd like to keep !work -. .fi -. .IP "" 0 -. +.P +If you want to open gitignore in an editor and do changes manually pass \-e flag: +.IP "" 4 +.nf +$ git ignore \-p \-e +.fi +.IP "" 0 .SH "AUTHOR" -Written by Tj Holowaychuk <\fItj@vision\-media\.ca\fR> and Tema Bolshakov <\fItweekane@gmail\.com\fR> and Nick Lombard <\fIgithub@jigsoft\.co\.za\fR> -. +Written by Tj Holowaychuk <\fItj@vision\-media\.ca\fR> and Tema Bolshakov <\fItweekane@gmail\.com\fR> and Nick Lombard <\fIgithub@jigsoft\.co\.za\fR> and Kirill Dergachev <\fIkdergachev1062@gmail\.com\fR> .SH "REPORTING BUGS" <\fIhttps://github\.com/tj/git\-extras/issues\fR> -. .SH "SEE ALSO" <\fIhttps://github\.com/tj/git\-extras\fR> diff --git a/man/git-ignore.html b/man/git-ignore.html index c3c44128..9589e241 100644 --- a/man/git-ignore.html +++ b/man/git-ignore.html @@ -1,9 +1,9 @@ - - - git-ignore(1) - Add .gitignore patterns + + + git-ignore(1) - Modify or display .gitignore files