Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@xkr47
Last active September 28, 2017 07:59
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xkr47/d8712409f84bebc4f79e43370a3902b4 to your computer and use it in GitHub Desktop.
Save xkr47/d8712409f84bebc4f79e43370a3902b4 to your computer and use it in GitHub Desktop.
git tweak-index - tweak/edit files in index without touching the checked out versions

git tweak-index

This git add-on command lets you tweak/edit files in the index/stage directly without touching the checked out working copy.

  • Use case 1: So you accidentally git-added some changes that you didn't want to merge just yet and would like to partially undo?
  • Use case 2: You want to make a quick commit to some file(s) but keep the checked out version(s) as-is?
  • Use case 3: You have been trying to use git reset -p but failed spectacularly?
  • Use case 4: You want to stage something that is not an interpolation of the checked out and the committed version?

Installation

Save the git-tweak-index.sh file in ~/bin/ or /usr/local/bin/ or something else in your PATH. Rename the file to not contain the .sh extension. Finally run chmod +rx git-tweak-index in chosen "bin" directory. Make sure you have a text editor of your choice set either through core.editor in your git config or through the EDITOR environment variable.

git config --global core.editor <editor>
# or
export EDITOR=<editor>
# or
export GIT_EDITOR=<editor>

Usage

git tweak-index myfile.txt
git tweak-index myfile.txt myfile2.txt

Example

$ git init
$ seq 1 10 > foo
$ cat foo
1
2
3
4
5
6
7
8
9
10
$ git add foo
$ git commit -m '1-10'
$ sed -i '/[567]/d' foo
$ git add foo 
$ git diff --staged | cat
diff --git foo foo
index f00c965..07c816d 100644
--- foo
+++ foo
@@ -2,9 +2,6 @@
 2
 3
 4
-5
-6
-7
 8
 9
 10
$ echo 42 >> foo
$ git add foo
$ git diff --staged
diff --git foo foo
index f00c965..04a8e20 100644
--- foo
+++ foo
@@ -2,9 +2,7 @@
 2
 3
 4
-5
-6
-7
 8
 9
 10
+42
# oops, I didn't really want to add that 42 to the index (not yet at least)
$ git tweak-index foo
# editor pops up, you remove "42" from the file, save, exit
$ git diff --staged | cat
diff --git foo foo
index f00c965..07c816d 100644
--- foo
+++ foo
@@ -2,9 +2,6 @@
 2
 3
 4
-5
-6
-7
 8
 9
 10
# looks great, 42 is no longer index/stage now, just what I wanted
$ git diff | cat
diff --git foo foo
index 07c816d..04a8e20 100644
--- foo
+++ foo
@@ -5,3 +5,4 @@
 8
 9
 10
+42
# .. but it is still there in my working copy, wonderful!
$ git status
On branch master                                                       
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   foo

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   foo

$ git commit -m 'remove 5-7'

Naming

I was going to call this edit-index but that was already taken, so I picked tweak-index instead. If you want to use some other name for it, just create an alias for it, for example to use edit-index:

git config --global alias.edit-index tweak-index
#!/bin/bash
# Copyright Jonas Berlin 2016
# LICENSE: MIT
# Source: https://gist.github.com/xkr47/d8712409f84bebc4f79e43370a3902b4
set -e
if [ $# = 0 ]; then
echo "Usage: git tweak-index <file-in-stage> [...]"
exit 0
fi
EDITOR="$(git var GIT_EDITOR ||:)"
if [ ! "$EDITOR" ]; then
echo "ERROR: Please set a) the 'core.editor' git config or b) the EDITOR or c) the GIT_EDITOR environment variable" >&2
exit 1
fi
topprefix="$(git rev-parse --show-cdup)"
subprefix="$(git rev-parse --show-prefix)"
for subpath ; do
toppath="${subprefix}${subpath}"
while read _mode _sha _stage _subpath ; do
if [ ! "$_stage" ]; then
echo "git tweak-index: $toppath is not in the cache"
err=1
elif [ "$_stage" != "0" ]; then
echo "git tweak-index: $toppath is unmerged"
err=1
fi
done <<< $(git ls-files --stage -- "$subpath")
done
if [ "$err" = "1" ]; then
exit 1
fi
for subpath ; do
toppath="${subprefix}${subpath}"
mode=
while read _mode _sha _stage _subpath ; do
if [ "$_stage" = "0" ]; then
mode="$_mode"
break
fi
done <<< $(git ls-files --stage -- "$subpath")
[ "$mode" ]
read toptmppath _subpath <<< $(git checkout-index --temp -- "$subpath")
subtmppath="${topprefix}${toptmppath}"
newsubtmppath="${subtmppath}_${subpath##*/}"
mv "$subtmppath" "$newsubtmppath"
subtmppath="$newsubtmppath"
$EDITOR "$subtmppath"
read newsha <<< $(git hash-object -w --path "$toppath" -- "$subtmppath")
git update-index --cacheinfo $mode,$newsha,"$toppath"
rm "$subtmppath"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment