-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 60d5da4
Showing
6 changed files
with
652 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
Copyright (c) 2015, Dan Aloni | ||
All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions are met: | ||
|
||
* Redistributions of source code must retain the above copyright notice, this | ||
list of conditions and the following disclaimer. | ||
|
||
* Redistributions in binary form must reproduce the above copyright notice, | ||
this list of conditions and the following disclaimer in the documentation | ||
and/or other materials provided with the distribution. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
About - git-bottle v0.1-rc | ||
-------------------------- | ||
|
||
git-bottle is an **experimental** utility for the purpose of saving and restoring the various ``git`` working states *as normal git commits*, effectively snapshotting the current and pertinent state of your working tree and various file states shown under ``git status``: | ||
|
||
* Modified but not staged | ||
* Staged but not commited | ||
* Untracked files that are not ignored via ``.gitignore`` | ||
* Unmerged paths (in all the various states - rebase, cherry-pick, or merge) | ||
* Rebase state | ||
* Rebase-interactive state | ||
* Merge state | ||
|
||
To illustrate how the commit history looks like when using ``git-bottle``. Suppose that we have a complex state during a merge: | ||
|
||
.. code-block:: console | ||
$ git status | ||
On branch master | ||
Your branch and 'origin/master' have diverged, | ||
and have 3 and 1 different commit each, respectively. | ||
(use "git pull" to merge the remote branch into yours) | ||
You have unmerged paths. | ||
(fix conflicts and run "git commit") | ||
Changes to be committed: | ||
modified: file-non-conflicting-2 | ||
Unmerged paths: | ||
(use "git add/rm <file>..." as appropriate to mark resolution) | ||
both modified: file | ||
deleted by us: file-will-be-removed-down | ||
deleted by them: file-will-be-removed-up | ||
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: file-non-conflicting-2 | ||
modified: file-non-conflicting-3 | ||
Untracked files: | ||
(use "git add <file>..." to include in what will be committed) | ||
untracked | ||
.. | ||
|
||
But we decide to put this merge aside for some reason. We use git-bottle, and the result is: | ||
|
||
.. code-block:: console | ||
$ git-bottle | ||
<some various prints here> | ||
$ git status | ||
On branch master | ||
Your branch is ahead of 'origin/master' by 4 commits. | ||
(use "git push" to publish your local commits) | ||
nothing to commit, working directory clean | ||
$ git-log --oneline --stat | ||
3251e3b1cf3 (HEAD, master) GIT_BOTTLE_STATE: untracked | ||
untracked | 3 +++ | ||
1 file changed, 3 insertions(+) | ||
1075c68e8448 GIT_BOTTLE_STATE: uncommitted | ||
file-non-conflicting-2 | 1 + | ||
file-non-conflicting-3 | 1 + | ||
2 files changed, 2 insertions(+) | ||
f7f6727daaa3 GIT_BOTTLE_STATE: staged | ||
file-non-conflicting-2 | 1 + | ||
1 file changed, 1 insertion(+) | ||
12992d33063b GIT_BOTTLE_STATE_MERGE: Merge remote-tracking branch 'origin/master' | ||
.GIT_BOTTLE_MERGE_HEAD | 1 + | ||
.GIT_BOTTLE_MERGE_MODE | 0 | ||
.GIT_BOTTLE_MERGE_MSG | 6 ++++++ | ||
file | 5 +++++ | ||
file-will-be-removed-down | 2 ++ | ||
file-will-be-removed-down.1.GIT_BOTTLE_UNMERGED_NORMALIZED | 1 + | ||
file-will-be-removed-down.3.GIT_BOTTLE_UNMERGED_NORMALIZED | 2 ++ | ||
file-will-be-removed-up.1.GIT_BOTTLE_UNMERGED_NORMALIZED | 1 + | ||
file-will-be-removed-up.2.GIT_BOTTLE_UNMERGED_NORMALIZED | 2 ++ | ||
file.1.GIT_BOTTLE_UNMERGED_NORMALIZED | 1 + | ||
file.2.GIT_BOTTLE_UNMERGED_NORMALIZED | 2 ++ | ||
file.3.GIT_BOTTLE_UNMERGED_NORMALIZED | 2 ++ | ||
12 files changed, 25 insertions(+) | ||
.. | ||
The branch now contains the needed data to restore both the working tree, index, and the meta-data regarding the merge. If we run ``git-unbottle``, we would find outselves back in the complex ``git status`` from above. The special commit messages assist ``git-unbottle`` in the work of unwinding the effects of ``git-bottle``. | ||
|
||
Main use case | ||
~~~~~~~~~~~~~ | ||
|
||
You find yourself working on resolving conflicts in a big merge or a rebase, but some of the conflicts are in other people's code. Those people are not around, or temporarily unavailble. You want to pass the torch of conflict resolution to the other person and shift to work on more interesting things. You are in the midst on the merge, 12 out of 30 files done, meaning you neither ``git-stash`` or a simple ``git-commit`` would get you out of that state. Well, you can work from another clone of the project, but your Eclipse or another IDE was rigged so perfectly to function properly from that single clone path. | ||
|
||
``git-bottle`` to the rescue - you bottle up the current unfinished merge, push it to say, by convention e.g. ``bottle/whatever-branch``, and notify the other person. The other guy will do ``git-fetch``, checkout that branch, perform ``git-unbottle``, and resume the merge. | ||
|
||
Conflict resolution in a separate commit | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
When merging, one might want to leave the merge commit as it is and solve the conflicts in a subsequent commit. This way, the conflict resolution is made more explict, cherry-pickable, etc. ``git-bottle`` facilitates with this approach. | ||
|
||
A better stash | ||
~~~~~~~~~~~~~~ | ||
|
||
Never forget again that you have made stashes. Instead, with ``git-bottle``, they could be local branches lying around. Hopefully, this demonstrates that by turning the index and many other working states into the first-class citizen ``commit object``, the flexibility of ``git`` can be improved. | ||
|
||
|
||
Installation | ||
------------ | ||
|
||
The ``git-*`` scripts from this repository need to be somewhere in ``$PATH`` for the commands to work. | ||
|
||
**Big fat warning** | ||
------------------- | ||
|
||
* ``git-bottle`` is sensitive to the versions of git program used because it saves some of the meta-data, especially during rebase and rebase-interactive. It might break if there is a mismatch of versions of the git program. I have tested it over Git 2.1.0. | ||
* Be careful with ``git-unbottle`` of states from untrusted sources! The state of rebase-interactive might contain bash scripts. | ||
* For devs - this is unfinished work. I am sorry if you find the code undocumented/unclear/buggy, but I'd consider every pull request. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
#!/bin/bash -ue | ||
|
||
# Utility functions | ||
cur_tree() { | ||
git show --format="%T" -s | ||
} | ||
|
||
# find normalization script | ||
NORMALIZATION_SCRIPT=git-unmerged-normalization | ||
set +e | ||
which ${NORMALIZATION_SCRIPT} 2>/dev/null 1>/dev/null | ||
if [ "0" != "$?" ] ; then | ||
set -e | ||
NORMALIZATION_SCRIPT=`dirname ${BASH_SOURCE}`/${NORMALIZATION_SCRIPT} | ||
else | ||
set -e | ||
NORMALIZATION_SCRIPT=`which ${NORMALIZATION_SCRIPT}` | ||
fi | ||
|
||
if [ ! -x ${NORMALIZATION_SCRIPT} ] ; then | ||
echo ${NORMALIZATION_SCRIPT} not found | ||
exit -1 | ||
fi | ||
|
||
bottle() { | ||
# Turn unmerged paths into a commit. Unless we do this | ||
# git likes to complain about the state of the index. It's | ||
# normallization 'normallization' of the index. | ||
|
||
${NORMALIZATION_SCRIPT} encode | ||
|
||
git_top=`git rev-parse --show-toplevel`/.git | ||
|
||
# Turn staged files into a commit | ||
tree=`cur_tree` | ||
git commit -m "GIT_BOTTLE_STATE: staged" --allow-empty > /dev/null | ||
if [ "${tree}" == "`cur_tree`" ] ; then | ||
# Remove empty commit | ||
git reset --soft HEAD^ | ||
else | ||
echo "git-bottle: commit created - staged changes" | ||
fi | ||
|
||
# Turn modified but unadded files into a commit | ||
tree=`cur_tree` | ||
git commit -a -m "GIT_BOTTLE_STATE: uncommitted" --allow-empty > /dev/null | ||
if [ "${tree}" == "`cur_tree`" ] ; then | ||
# Remove empty commit | ||
git reset --soft HEAD^ | ||
else | ||
echo "git-bottle: commit created - unstaged changes to tracked files" | ||
fi | ||
|
||
# Turn untracked but not ignored files a commit | ||
tree=`cur_tree` | ||
git add -A . | ||
git commit -m "GIT_BOTTLE_STATE: untracked" --allow-empty > /dev/null | ||
if [ "${tree}" == "`cur_tree`" ] ; then | ||
# Remove empty commit | ||
git reset --soft HEAD^ | ||
else | ||
echo "git-bottle: commit created - untracked files" | ||
fi | ||
|
||
commit_special_state() { | ||
state=$1 | ||
branch=`cat ${git_top}/${state}/head-name` | ||
branch=${branch#refs/heads/} | ||
gitversion=`git --version` | ||
|
||
echo Detected ${state} | ||
mv ${git_top}/${state} .GIT-BOTTLE-STATE-${state} | ||
|
||
git add -f .GIT-BOTTLE-STATE-${state} | ||
|
||
echo -e "GIT_BOTTLE_STATE: ${state}\n" > ${git_top}/TEMP_MSG | ||
echo "Branch: ${branch}" >> ${git_top}/TEMP_MSG | ||
echo "Git-Version: ${gitversion}" >> ${git_top}/TEMP_MSG | ||
|
||
git commit -F ${git_top}/TEMP_MSG --allow-empty | ||
rm -f ${git_top}/TEMP_MSG | ||
rev=$(git rev-parse HEAD) | ||
git checkout ${branch} 2>/dev/null >/dev/null | ||
git reset --hard ${rev} | ||
} | ||
|
||
# Turn various special states into a commit | ||
if [[ -f ${git_top}/rebase-merge/head-name ]] ; then | ||
commit_special_state rebase-merge | ||
elif [[ -f ${git_top}/rebase-apply/head-name ]] ; then | ||
commit_special_state rebase-apply | ||
fi | ||
} | ||
|
||
unbottle() { | ||
git_top=`git rev-parse --show-toplevel`/.git | ||
|
||
subj=`git show HEAD --format="%s" -s` | ||
if [ "${subj}" == "GIT_BOTTLE_STATE: rebase-merge" ] ; then | ||
echo Detected rebase-merge | ||
git checkout `git rev-parse HEAD` 2>/dev/null | ||
git reset --soft HEAD^ | ||
git reset HEAD . | ||
mv .GIT-BOTTLE-STATE-rebase-merge ${git_top}/rebase-merge | ||
elif [ "${subj}" == "GIT_BOTTLE_STATE: rebase-apply" ] ; then | ||
echo Detected rebase-apply | ||
git checkout `git rev-parse HEAD` 2>/dev/null | ||
git reset --soft HEAD^ | ||
git reset HEAD . | ||
mv .GIT-BOTTLE-STATE-rebase-apply ${git_top}/rebase-apply | ||
fi | ||
|
||
subj=`git show HEAD --format="%s" -s` | ||
if [ "${subj}" == "GIT_BOTTLE_STATE: untracked" ] ; then | ||
git reset --soft HEAD^ | ||
git reset HEAD . | ||
fi | ||
|
||
subj=`git show HEAD --format="%s" -s` | ||
if [ "${subj}" == "GIT_BOTTLE_STATE: uncommitted" ] ; then | ||
git reset --soft HEAD^ | ||
git reset HEAD . | ||
fi | ||
|
||
subj=`git show HEAD --format="%s" -s` | ||
if [ "${subj}" == "GIT_BOTTLE_STATE: staged" ] ; then | ||
git reset --soft HEAD^ | ||
fi | ||
|
||
${NORMALIZATION_SCRIPT} decode | ||
} | ||
|
||
cmd=bottle | ||
|
||
while getopts "uh" opt; do | ||
case $opt in | ||
u) | ||
cmd=unbottle | ||
;; | ||
h) | ||
echo "${BASH_SOURCE} [-u]" | ||
exit 1 | ||
;; | ||
\?) | ||
echo "invalid option ${opt}" | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
|
||
${cmd} "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/bin/bash -ue | ||
|
||
`dirname ${BASH_SOURCE}`/git-bottle -u "$@" |
Oops, something went wrong.