git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 21/23] Introduce git-atomic.

From
Jon Seymour <jon.seymour@gmail.com>
Date
Apr 23, 2011, 07:22 UTC
Message-ID
<1303543372-77843-22-git-send-email-jon.seymour@gmail.com>
In-Reply-To
<1303543372-77843-1-git-send-email-jon.seymour@gmail.com>

git atomic is intended to provide a simple way to for porcelains to execute atomic operations on the git repository.

An atomic operation either completes successfully or the working tree, index and selected references are returned to their original state.

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
 .gitignore                   |    2 +
 Documentation/git-atomic.txt |   92 ++++++++++++++++++++++++++++++++++++++++++
 Makefile                     |    2 +
 git-atomic-lib.sh            |   58 ++++++++++++++++++++++++++
 git-atomic.sh                |    5 ++
 t/t3419-atomic.sh            |   59 +++++++++++++++++++++++++++
 6 files changed, 218 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/git-atomic.txt
 create mode 100644 git-atomic-lib.sh
 create mode 100755 git-atomic.sh
 create mode 100755 t/t3419-atomic.sh
diff --git a/.gitignore b/.gitignore
index aa0eb8fb..5efc43c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,8 @@
 /git-apply
 /git-archimport
 /git-archive
+/git-atomic
+/git-atomic-lib
 /git-bisect
 /git-bisect--helper
 /git-blame
diff --git a/Documentation/git-atomic.txt b/Documentation/git-atomic.txt
new file mode 100644
index 0000000..02bbee6
--- /dev/null
+++ b/Documentation/git-atomic.txt
@@ -0,0 +1,92 @@
+git-atomic(1)
+===========
+
+NAME
+----
+git-atomic - conditionally execute a command and, if it fails, restore the working tree, index and HEAD to their original state.
+
+SYNOPSIS
+--------
+[verse]
+'git atomic' [options] [--] [cmd args...]
+. $(git --exec-path)/git-atomic-lib.sh
+
+DESCRIPTION
+-----------
+The first form conditionally executes a command depending on whether the pre-conditions are satisified. If the command exits with a non-zero exit code, restores the
+working tree, index and HEAD to their orignal state. If no command is specified, sets the exit code according to the pre-conditions.
+
+'git atomic' exits with:
+
+0::
+	if the pre-conditions are satisified and the command executes successfully
+128::
+	if the command exited with an exit code of 128, 129, 130 or 131.
+129::
+	if git atomic failed during setup or argument parsing
+130::
+	if the pre-conditions were not satisified.
+131::
+	if the command failed and the original state could not be restored.
+
+Otherwise, exits with the actual exit code of the command.
+
+The second form is used to import the definition of a shell function called atomic that can be called by shell scripts that perform git operations. This is useful when the operations to be given atomic behaviour are themselves shell functions rather than external commands.
+
+OPTIONS
+-------
+The following options specify pre-condition tests on various lists which are assumed to be empty in the clean state and non-empty in the dirty state.
+
+The default required state for each specified pre-condition option is clean. The default required state for each unspecified pre-condition option is any, which
+means no tests are applied.
+
+--unstaged [any|clean|dirty]::
+	Fail unless the list of unstaged changes is empty (clean) or non-empty (dirty).	
+--staged   [any|clean|dirty]::
+	Fail unless the list of staged changes is empty (clean) or non-empty (dirty).
+--untracked [any|clean|dirty]::
+	Fail unless the list of untracked files is empty (clean) or non-empty (dirty).
+--tracked [any|clean|dirty]::
+	Fail unless the list of staged and unstaged changes is empty (clean) or non-empty (dirty).
+--unmerged [any|clean|dirty]::
+	Fail unless the list of unmerged files is empty (clean) or non-empty (dirty).
+--rebase [any|clean|dirty]::
+	Fail unless a rebase is not (clean) or is (dirty) in progress.
+--all::
+	Fail unless the list of staged, unstaged changes and untracked files is empty (clean) or non-empty (dirty)
+
+EXAMPLES
+--------
+* Reset the tree only if there are no staged or unstaged changes and no untracked files.
++
+-----------
+git atomic --all clean git reset --hard upstream/master
+-----------
+* Conditionally perform a merge, but rollback and die if it fails.
++
+-----------
+git atomic --all clean git merge topic || die "unable to merge"
+-----------
+* Import git-atomic-lib.sh into a script and make the execution of the function foo atomic with respect to the state of the git workspace
++
+-----------
+. $(git --exec-path)/git-atomic-lib.sh
+foo()
+{
+    git merge $1
+}
+
+atomic foo $1
+-----------
+
+Author
+------
+Written by Jon Seymour <jon.seymour@gmail.com>
+
+Documentation
+-------------
+Documentation by Jon Seymour
+
+GIT
+---
+Part of the linkgit:git[7] suite
diff --git a/Makefile b/Makefile
index 93ff6c6..9ed877a 100644
--- a/Makefile
+++ b/Makefile
@@ -360,6 +360,7 @@ TEST_PROGRAMS_NEED_X =
 unexport CDPATH
 
 SCRIPT_SH += git-am.sh
+SCRIPT_SH += git-atomic.sh
 SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
@@ -379,6 +380,7 @@ SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-test.sh
 SCRIPT_SH += git-web--browse.sh
 
+SCRIPT_LIB += git-atomic-lib
 SCRIPT_LIB += git-conditions-lib
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
diff --git a/git-atomic-lib.sh b/git-atomic-lib.sh
new file mode 100644
index 0000000..db48300
--- /dev/null
+++ b/git-atomic-lib.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Provides a function that provides for robust recovery from
+#
+. git-test-lib
+atomic()
+{
+    assert --not-conflicted --message "cannot perform an atomic operation while there are merge conflicts"
+    HEAD=$(git rev-parse --verify HEAD) || setup_failed "failed to resolve HEAD"
+    if REF=$(git symbolic-ref -q HEAD)
+    then
+          BRANCH=${REF#refs/heads/}
+    else
+        BRANCH=${HEAD}
+    fi
+
+    STASH=$(git stash create) || setup_failed "failed to stash"
+    REF=$(git rev-parse --symbolic-full-name HEAD) || setup_failed "failed to acquire REF"
+    REBASE_COUNT=1
+    test_condition -q --rebasing || REBASE_COUNT=0
+
+    (
+      "$@"
+    ) || (
+       RC=$?
+
+       command_failed()
+       {
+           rc=$1
+	   shift
+           echo "command failed: $* rc=$rc" 1>&2
+           exit 1
+       }
+
+       restore_failed()
+       {
+           echo "restore failed: $*" 1>&2
+           exit 2
+       }
+
+       if test $REBASE_COUNT -eq 0 && test -d "$REBASE_DIR"
+       then
+            git rebase --abort || restore_failed "failed to abort rebase"
+       fi
+
+	{
+		git reset --hard HEAD &&
+		git checkout -q ${BRANCH}
+	} || restore_failed "failed to checkout ${BRANCH}"
+
+	if test -n "$STASH"
+	then
+		git stash apply --index "$STASH" || restore_failed "failed to reapply stash $STASH to $HEAD"
+		echo "restored $REF to $(git describe --always --abbrev=6 $HEAD), reapplied stash $(git describe --always --abbrev=6 $STASH)" 1>&2
+	fi
+	command_failed $RC "$*"
+    )
+}
diff --git a/git-atomic.sh b/git-atomic.sh
new file mode 100755
index 0000000..aa14e68
--- /dev/null
+++ b/git-atomic.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+SUBDIRECTORY_OK=true
+. git-sh-setup
+. git-atomic-lib
+atomic "$@"
diff --git a/t/t3419-atomic.sh b/t/t3419-atomic.sh
new file mode 100755
index 0000000..2b651d2
--- /dev/null
+++ b/t/t3419-atomic.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Jon Seymour
+#
+
+test_description='git atomic tests
+
+Performs tests on the functions of git atomic
+'
+. ./test-lib.sh
+
+GIT_AUTHOR_NAME=author@name
+GIT_AUTHOR_EMAIL=bogus@email@address
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+
+test_expect_success \
+    'setup' \
+    '
+    test_commit A &&
+    test_commit B &&
+    test_commit C &&
+    test_commit D &&
+    git checkout -b branch A &&
+    test_commit X &&
+    echo >> B.t &&
+    git add B.t &&
+    test_commit Y
+    true
+'
+
+test_expect_success 'no arguments' '
+    git atomic
+'
+
+test_expect_success 'successful command' \
+'
+    git atomic true
+'
+
+test_expect_success 'unsuccessful command' \
+'
+    ! git atomic false
+'
+
+test_expect_success 'rebase' \
+'
+     git reset --hard HEAD &&
+     git checkout master &&
+     MASTER=$(git rev-parse HEAD) &&
+     ! git rebase --onto D A Y &&
+     git test --conflicted &&
+     git rebase --abort &&
+     git checkout master &&
+     ! git atomic git rebase --onto D A Y &&
+     git test --same HEAD refs/heads/master &&
+     git test --same HEAD $MASTER
+'
+
+test_done
-- 
1.7.5.rc1.23.g7f622
Previous: Jon SeymourNext: Jon Seymour
Message 23 of 29 in “RFC: Introducing git-test, git-atomic, git-base and git-work”
  1. 00/23 RFC: Introducing git-test, git-atomic, git-base and git-workJon Seymour, Apr 23, 2011
  2. 01/23 Introduce git-test.sh and git-test-lib.shJon Seymour, Apr 23, 2011
  3. Drew NorthupApr 27, 2011
  4. 02/23 Introduce --unstaged.Jon Seymour, Apr 23, 2011
  5. 03/23 Introduce --stagedJon Seymour, Apr 23, 2011
  6. 04/23 Introduce --untracked.Jon Seymour, Apr 23, 2011
  7. 05/23 Introduce --conflictedJon Seymour, Apr 23, 2011
  8. 06/23 Introduce --rebasingJon Seymour, Apr 23, 2011
  9. 07/23 Introduce --detachedJon Seymour, Apr 23, 2011
  10. 08/23 Introduce --branch-existsJon Seymour, Apr 23, 2011
  11. 09/23 Introduce --tag-existsJon Seymour, Apr 23, 2011
  12. 10/23 Introduce --ref-existsJon Seymour, Apr 23, 2011
  13. 11/23 Introduce --commit-exists.Jon Seymour, Apr 23, 2011
  14. 12/23 Introduce --checked-outJon Seymour, Apr 23, 2011
  15. 13/23 Introduce --reachableJon Seymour, Apr 23, 2011
  16. 14/23 Introduce --tree-same.Jon Seymour, Apr 23, 2011
  17. 15/23 Introduce --sameJon Seymour, Apr 23, 2011
  18. 16/23 miscJon Seymour, Apr 23, 2011
  19. 17/23 whitespace fix.Jon Seymour, Apr 23, 2011
  20. 18/23 tests --conflicted.Jon Seymour, Apr 23, 2011
  21. 19/23 rebasing: add testsJon Seymour, Apr 23, 2011
  22. 20/23 test: git test cleanups.Jon Seymour, Apr 23, 2011
  23. 21/23 Introduce git-atomic.Jon Seymour, Apr 23, 2011
  24. 22/23 Introduce git base.Jon Seymour, Apr 23, 2011
  25. 23/23 Introduce support for the git-work command.Jon Seymour, Apr 23, 2011
  26. Peter BaumannApr 23, 2011
  27. Jon SeymourApr 23, 2011
  28. Jon SeymourApr 23, 2011
  29. Jon SeymourApr 24, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.