Subject: [RFE/PATCH] git-fetch-rebase

From: José Fonseca <[email-masked]>
Date: Fri, 14 Dec 2007 22:12:31 +0000

Hi,

For those who use git as a better centralized SCM, it became a common
practice to do git-fetch + git-rebase instead of just git-pull, so that
the history is linear, instead of being full of tiny merges. 

However, there is no git command for such common procedure (at least
AFAIK). Attached is a variation of the git-pull porcelain (taken from
git version debian.1.5.3.7.1-dirty) that instead of doing git-fetch +
git-merge, it does git-fetch + git-rebase.

I've been using this git-fetch-rebase script frequently, and it really
boosts my productivity (I use it when updating from the main repository,
and before pushing my changes to the main repository).

I'd like see this script, or an equivalent command, included the git
distribution.

Cheers,

José Fonseca
--- /usr/bin/git-pull	2007-12-04 09:46:21.000000000 +0000
+++ git-fetch-rebase	2007-12-13 12:38:52.000000000 +0000
@@ -1,34 +1,25 @@
 #!/bin/sh
 #
+# Copyright (c) 2007 JosÃ© Fonseca
 # Copyright (c) 2005 Junio C Hamano
 #
-# Fetch one or more remote refs and merge it/them into the current HEAD.
+# Fetch a remote ref and rebase the current HEAD on top of it.
 
-USAGE='[-n | --no-summary] [--no-commit] [-s strategy]... [<fetch-options>] <repo> <head>...'
-LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
+USAGE='[-s strategy]... [<fetch-options>] <repo> <head>'
+LONG_USAGE='Fetch a remote ref and rebase the current HEAD onto it.'
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
-set_reflog_action "pull $*"
+set_reflog_action "fetch-rebase $*"
 require_work_tree
 cd_to_toplevel
 
 test -z "$(git ls-files -u)" ||
 	die "You are in the middle of a conflicted merge."
 
-strategy_args= no_summary= no_commit= squash=
+strategy_args=
 while :
 do
 	case "$1" in
-	-n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
-		--no-summa|--no-summar|--no-summary)
-		no_summary=-n ;;
-	--summary)
-		no_summary=$1
-		;;
-	--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
-		no_commit=--no-commit ;;
-	--sq|--squ|--squa|--squas|--squash)
-		squash=--squash ;;
 	-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
 		--strateg=*|--strategy=*|\
 	-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
@@ -55,30 +46,7 @@
 done
 
 orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
-git-fetch --update-head-ok "$@" || exit 1
-
-curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
-if test "$curr_head" != "$orig_head"
-then
-	# The fetch involved updating the current branch.
-
-	# The working tree and the index file is still based on the
-	# $orig_head commit, but we are merging into $curr_head.
-	# First update the working tree to match $curr_head.
-
-	echo >&2 "Warning: fetch updated the current branch head."
-	echo >&2 "Warning: fast forwarding your working tree from"
-	echo >&2 "Warning: commit $orig_head."
-	git update-index --refresh 2>/dev/null
-	git read-tree -u -m "$orig_head" "$curr_head" ||
-		die 'Cannot fast-forward your working tree.
-After making sure that you saved anything precious from
-$ git diff '$orig_head'
-output, run
-$ git reset --hard
-to recover.'
-
-fi
+git-fetch "$@" || exit 1
 
 merge_head=$(sed -e '/	not-for-merge	/d' \
 	-e 's/	.*//' "$GIT_DIR"/FETCH_HEAD | \
@@ -90,21 +58,21 @@
 	case $? in
 	  0) ;;
 	  1) echo >&2 "You are not currently on a branch; you must explicitly"
-	     echo >&2 "specify which branch you wish to merge:"
-	     echo >&2 "  git pull <remote> <branch>"
+	     echo >&2 "specify which branch you wish to rebase onto:"
+	     echo >&2 "  git fetch-rebase <remote> <branch>"
 	     exit 1;;
 	  *) exit $?;;
 	esac
 	curr_branch=${curr_branch#refs/heads/}
 
-	echo >&2 "You asked me to pull without telling me which branch you"
-	echo >&2 "want to merge with, and 'branch.${curr_branch}.merge' in"
+	echo >&2 "You asked me to rebase without telling me which branch you"
+	echo >&2 "want to rebase, and 'branch.${curr_branch}.merge' in"
 	echo >&2 "your configuration file does not tell me either.  Please"
-	echo >&2 "name which branch you want to merge on the command line and"
-	echo >&2 "try again (e.g. 'git pull <repository> <refspec>')."
+	echo >&2 "name which branch you want to rebase on the command line and"
+	echo >&2 "try again (e.g. 'git fetch-rebase <repository> <refspec>')."
 	echo >&2 "See git-pull(1) for details on the refspec."
 	echo >&2
-	echo >&2 "If you often merge with the same branch, you may want to"
+	echo >&2 "If you often rebase to the same branch, you may want to"
 	echo >&2 "configure the following variables in your configuration"
 	echo >&2 "file:"
 	echo >&2
@@ -117,21 +85,15 @@
 	exit 1
 	;;
 ?*' '?*)
-	if test -z "$orig_head"
-	then
-		echo >&2 "Cannot merge multiple branches into empty head"
-		exit 1
-	fi
+	echo >&2 "Cannot rebase onto multiple branches"
+	exit 1
 	;;
 esac
 
 if test -z "$orig_head"
 then
-	git update-ref -m "initial pull" HEAD $merge_head "" &&
-	git read-tree --reset -u HEAD || exit 1
-	exit
+	echo >&2 "Cannot rebase empty head"
+	exit 1
 fi
 
-merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
-exec git-merge $no_summary $no_commit $squash $strategy_args \
-	"$merge_name" HEAD $merge_head
+exec git-rebase $strategy_args $merge_head