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

[PATCH 3/3] pull: introduce --[no-]autostash and pull.autostash

From
Ramkumar Ramachandra <artagnon@gmail.com>
Date
Apr 13, 2013, 21:15 UTC
Message-ID
<1365887729-9630-4-git-send-email-artagnon@gmail.com>
In-Reply-To
<1365887729-9630-1-git-send-email-artagnon@gmail.com>

Currently, executing a 'git pull' on a dirty worktree gives the following annoying message:

    # User doesn't notice dirty worktree
    $ git pull
    ... # fetch operation
    error: Your local changes to the following files would be overwritten by merge:
        quux
    Please, commit your changes or stash them before you can merge.
    Aborting

At which point the user will stash her changes, re-execute git pull, and pop the stash. This process can easily be automated out for a smooth:

    $ git pull
    ... # fetch operation
    Saved working directory and index state WIP on master: 8ea73ed baz
    HEAD is now at 8ea73ed baz
    ... # The merge/rebase process
    Dropped refs/stash@{0} (83f47fbfb07a741817633f9191dc4a1530f79249)

If there is a conflict during the merge/rebase process, the user has to pop the stash by hand after committing the conflict resolution:

    $ git pull
    ... # fetch operation
    Saved working directory and index state WIP on master: 8ea73ed baz
    HEAD is now at 8ea73ed baz
    ... # The merge/rebase process
    Automatic merge failed; fix conflicts and then commit the result.
    Please run 'git stash pop' after commiting the conflict resolution.

Introduce a new configuration variable pull.autostash that does exactly this.

Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
 Documentation/config.txt   |  5 ++++
 Documentation/git-pull.txt |  7 ++++++
 git-pull.sh                | 33 ++++++++++++++++++++++--
 t/t5521-pull-options.sh    | 63 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 106 insertions(+), 2 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 3d750e0..6c5cd8e 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1804,6 +1804,11 @@ pull.rebase::
 it unless you understand the implications (see linkgit:git-rebase[1]
 for details).
 
+pull.autostash::
+	When true, automatically stash all changes before attempting
+	to merge/rebase, and pop the stash after a successful
+	merge/rebase.
+
 pull.octopus::
 	The default merge strategy to use when pulling multiple branches
 	at once.
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 24ab07a..1c5384b 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -94,6 +94,13 @@ must be given before the options meant for 'git fetch'.
 	has to be called afterwards to bring the work tree up to date with the
 	merge result.
 
+--[no-]autostash::
+	When turned on, automatically stash all changes before
+	attempting to merge/rebase, and pop the stash after a
+	successful merge/rebase.  Useful for people who want to pull
+	with a dirty worktree.  This option can also be set via the
+	`pull.autostash` configuration variable.
+
 Options related to merging
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/git-pull.sh b/git-pull.sh
index 5fe69fa..4edc06a 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -48,6 +48,7 @@ if test -z "$rebase"
 then
 	rebase=$(git config --bool pull.rebase)
 fi
+autostash=$(git config --bool pull.autostash || echo false)
 dry_run=
 while :
 do
@@ -116,6 +117,12 @@ do
 	--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)
 		rebase=false
 		;;
+	--autostash)
+		autostash=true
+		;;
+	--no-autostash)
+		autostash=false
+		;;
 	--recurse-submodules)
 		recurse_submodules=--recurse-submodules
 		;;
@@ -202,7 +209,8 @@ test true = "$rebase" && {
 		then
 			die "$(gettext "updating an unborn branch with changes added to the index")"
 		fi
-	else
+	elif test "$autostash" = false
+	then
 		require_clean_work_tree "pull with rebase" "Please commit or stash them."
 	fi
 	oldremoteref= &&
@@ -219,6 +227,12 @@ test true = "$rebase" && {
 		fi
 	done
 }
+
+stash_required () {
+	! (git diff-files --quiet --ignore-submodules &&
+	git diff-index --quiet --cached HEAD --ignore-submodules)
+}
+
 orig_head=$(git rev-parse -q --verify HEAD)
 git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok "$@" || exit 1
 test -z "$dry_run" || exit 0
@@ -294,4 +308,19 @@ true)
 	eval="$eval \"\$merge_name\" HEAD $merge_head"
 	;;
 esac
-eval "exec $eval"
+
+if test "$autostash" = true && stash_required
+then
+	git stash || die "$(gettext "Cannot autostash")" &&
+	require_clean_work_tree "pull" "Please commit or stash them." &&
+	if eval "$eval"
+	then
+		git stash pop || die "$(gettext "Cannot pop stash")"
+	else
+		exit_code=$?
+		echo "Please run 'git stash pop' after commiting the conflict resolution."
+		exit $exit_code
+	fi
+else
+	eval "exec $eval"
+fi
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 3bdfe82..4545671 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -117,4 +117,67 @@ test_expect_success 'git pull --all' '
 	)
 '
 
+test_expect_success 'pull --autostash with clean worktree' '
+	mkdir clonedautostash &&
+	(
+		cd clonedautostash &&
+		git init &&
+		git pull --autostash ../parent &&
+		test_must_fail test_path_is_file .git/refs/stash
+		test_commit one
+	) &&
+	rm -rf clonedautostash
+'
+
+test_expect_success 'pull.autostash with clean worktree' '
+	mkdir clonedautostash &&
+	(
+		cd clonedautostash &&
+		git init &&
+		test_config pull.autostash true &&
+		git pull ../parent &&
+		test_must_fail test_path_is_file .git/refs/stash
+		test_commit one
+	) &&
+	rm -rf clonedautostash
+'
+
+test_expect_success 'pull.autostash without conflict' '
+	mkdir clonedautostash &&
+	(
+		cd clonedautostash &&
+		git init &&
+		test_commit "root_commit" &&
+		cat >quux <<-\EOF &&
+		this is a non-conflicting file
+		EOF
+		git add quux &&
+		test_config pull.autostash true &&
+		git pull ../parent &&
+		test_must_fail test_path_is_file .git/refs/stash &&
+		test_path_is_file quux &&
+		test_commit one
+	) &&
+	rm -rf clonedautostash
+'
+
+test_expect_success 'pull.autostash with conflict' '
+	mkdir clonedautostash &&
+	(
+		cd clonedautostash &&
+		git init &&
+		test_commit "will_conflict" file "this is a conflicting file" &&
+		cat >quux <<-\EOF &&
+		this is a non-conflicting file
+		EOF
+		git add quux &&
+		test_config pull.autostash true &&
+		test_must_fail git pull ../parent &&
+		test_must_fail test_commit one &&
+		test_path_is_file .git/refs/stash &&
+		test_must_fail test_path_is_file quux
+	) &&
+	rm -rf clonedautostash
+'
+
 test_done
-- 
1.8.2.1.392.g6943ea6
Previous: Ramkumar RamachandraNext: Junio C Hamano
Message 5 of 44 in “Introduce pull.autostash”
  1. 0/3 Introduce pull.autostashRamkumar Ramachandra, Apr 13, 2013
  2. 1/3 pull: prefer invoking "git <command>" over "git-<command>"Ramkumar Ramachandra, Apr 13, 2013
  3. Philip OakleyApr 14, 2013
  4. 2/3 t5521 (pull-options): use test_commit() where appropriateRamkumar Ramachandra, Apr 13, 2013
  5. 3/3 pull: introduce --[no-]autostash and pull.autostashRamkumar Ramachandra, Apr 13, 2013
  6. Junio C HamanoApr 15, 2013
  7. Ramkumar RamachandraApr 15, 2013
  8. Junio C HamanoApr 15, 2013
  9. Ramkumar RamachandraApr 15, 2013
  10. Matthieu MoyApr 15, 2013
  11. Junio C HamanoApr 15, 2013
  12. Matthieu MoyApr 15, 2013
  13. Ramkumar RamachandraApr 15, 2013
  14. Matthieu MoyApr 15, 2013
  15. Ramkumar RamachandraApr 15, 2013
  16. Matthieu MoyApr 15, 2013
  17. Ramkumar RamachandraApr 15, 2013
  18. Jonathan NiederApr 16, 2013
  19. Ramkumar RamachandraApr 15, 2013
  20. Matthieu MoyApr 15, 2013
  21. Ramkumar RamachandraApr 15, 2013
  22. Ramkumar RamachandraApr 15, 2013
  23. Matthieu MoyApr 15, 2013
  24. Junio C HamanoApr 15, 2013
  25. Ramkumar RamachandraApr 15, 2013
  26. John KeepingApr 15, 2013
  27. Ramkumar RamachandraApr 15, 2013
  28. Jonathan NiederApr 16, 2013
  29. Ramkumar RamachandraApr 16, 2013
  30. Jonathan NiederApr 16, 2013
  31. Junio C HamanoApr 15, 2013
  32. Ramkumar RamachandraApr 15, 2013
  33. Junio C HamanoApr 15, 2013
  34. Ramkumar RamachandraApr 15, 2013
  35. John KeepingApr 15, 2013
  36. Matthieu MoyApr 15, 2013
  37. Ramkumar RamachandraApr 16, 2013
  38. Matthieu MoyApr 16, 2013
  39. Ramkumar RamachandraApr 16, 2013
  40. Matthieu MoyApr 16, 2013
  41. Matthieu MoyApr 16, 2013
  42. Junio C HamanoApr 16, 2013
  43. Phil HordApr 16, 2013
  44. Ramkumar RamachandraApr 16, 2013

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.