Merge branch 'jc/merge' into next

* jc/merge:
  git-merge knows some strategies want to skip trivial merges
  generate-cmdlist: style cleanups.
  Add missing semicolon to sed command.
  unpack_delta_entry(): reduce memory footprint.
  git.el: Added a function to diff against the other heads in a merge.
  git.el: Get the default user name and email from the repository config.
  git.el: More robust handling of subprocess errors when returning strings.
maint
Junio C Hamano 2006-03-20 00:51:07 -08:00
commit e2fc650eef
4 changed files with 90 additions and 56 deletions

View File

@ -59,14 +59,14 @@


(defcustom git-committer-name nil (defcustom git-committer-name nil
"User name to use for commits. "User name to use for commits.
The default is to fall back to `add-log-full-name' and then `user-full-name'." The default is to fall back to the repository config, then to `add-log-full-name' and then to `user-full-name'."
:group 'git :group 'git
:type '(choice (const :tag "Default" nil) :type '(choice (const :tag "Default" nil)
(string :tag "Name"))) (string :tag "Name")))


(defcustom git-committer-email nil (defcustom git-committer-email nil
"Email address to use for commits. "Email address to use for commits.
The default is to fall back to `add-log-mailing-address' and then `user-mail-address'." The default is to fall back to the git repository config, then to `add-log-mailing-address' and then to `user-mail-address'."
:group 'git :group 'git
:type '(choice (const :tag "Default" nil) :type '(choice (const :tag "Default" nil)
(string :tag "Email"))) (string :tag "Email")))
@ -148,6 +148,12 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add
(append (git-get-env-strings env) (list "git") args)) (append (git-get-env-strings env) (list "git") args))
(apply #'call-process "git" nil buffer nil args))) (apply #'call-process "git" nil buffer nil args)))


(defun git-call-process-env-string (env &rest args)
"Wrapper for call-process that sets environment strings, and returns the process output as a string."
(with-temp-buffer
(and (eq 0 (apply #' git-call-process-env t env args))
(buffer-string))))

(defun git-run-process-region (buffer start end program args) (defun git-run-process-region (buffer start end program args)
"Run a git process with a buffer region as input." "Run a git process with a buffer region as input."
(let ((output-buffer (current-buffer)) (let ((output-buffer (current-buffer))
@ -189,13 +195,15 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add


(defun git-get-string-sha1 (string) (defun git-get-string-sha1 (string)
"Read a SHA1 from the specified string." "Read a SHA1 from the specified string."
(let ((pos (string-match "[0-9a-f]\\{40\\}" string))) (and string
(and pos (substring string pos (match-end 0))))) (string-match "[0-9a-f]\\{40\\}" string)
(match-string 0 string)))


(defun git-get-committer-name () (defun git-get-committer-name ()
"Return the name to use as GIT_COMMITTER_NAME." "Return the name to use as GIT_COMMITTER_NAME."
; copied from log-edit ; copied from log-edit
(or git-committer-name (or git-committer-name
(git-repo-config "user.name")
(and (boundp 'add-log-full-name) add-log-full-name) (and (boundp 'add-log-full-name) add-log-full-name)
(and (fboundp 'user-full-name) (user-full-name)) (and (fboundp 'user-full-name) (user-full-name))
(and (boundp 'user-full-name) user-full-name))) (and (boundp 'user-full-name) user-full-name)))
@ -204,6 +212,7 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add
"Return the email address to use as GIT_COMMITTER_EMAIL." "Return the email address to use as GIT_COMMITTER_EMAIL."
; copied from log-edit ; copied from log-edit
(or git-committer-email (or git-committer-email
(git-repo-config "user.email")
(and (boundp 'add-log-mailing-address) add-log-mailing-address) (and (boundp 'add-log-mailing-address) add-log-mailing-address)
(and (fboundp 'user-mail-address) (user-mail-address)) (and (fboundp 'user-mail-address) (user-mail-address))
(and (boundp 'user-mail-address) user-mail-address))) (and (boundp 'user-mail-address) user-mail-address)))
@ -259,18 +268,17 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add
(defun git-rev-parse (rev) (defun git-rev-parse (rev)
"Parse a revision name and return its SHA1." "Parse a revision name and return its SHA1."
(git-get-string-sha1 (git-get-string-sha1
(with-output-to-string (git-call-process-env-string nil "rev-parse" rev)))
(with-current-buffer standard-output
(git-call-process-env t nil "rev-parse" rev))))) (defun git-repo-config (key)
"Retrieve the value associated to KEY in the git repository config file."
(let ((str (git-call-process-env-string nil "repo-config" key)))
(and str (car (split-string str "\n")))))


(defun git-symbolic-ref (ref) (defun git-symbolic-ref (ref)
"Wrapper for the git-symbolic-ref command." "Wrapper for the git-symbolic-ref command."
(car (let ((str (git-call-process-env-string nil "symbolic-ref" ref)))
(split-string (and str (car (split-string str "\n")))))
(with-output-to-string
(with-current-buffer standard-output
(git-call-process-env t nil "symbolic-ref" ref)))
"\n")))


(defun git-update-ref (ref val &optional oldval) (defun git-update-ref (ref val &optional oldval)
"Update a reference by calling git-update-ref." "Update a reference by calling git-update-ref."
@ -285,11 +293,7 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add
(defun git-write-tree (&optional index-file) (defun git-write-tree (&optional index-file)
"Call git-write-tree and return the resulting tree SHA1 as a string." "Call git-write-tree and return the resulting tree SHA1 as a string."
(git-get-string-sha1 (git-get-string-sha1
(with-output-to-string (git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree")))
(with-current-buffer standard-output
(git-call-process-env t
(if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil)
"write-tree")))))


(defun git-commit-tree (buffer tree head) (defun git-commit-tree (buffer tree head)
"Call git-commit-tree with buffer as input and return the resulting commit SHA1." "Call git-commit-tree with buffer as input and return the resulting commit SHA1."
@ -763,6 +767,16 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add
(git-setup-diff-buffer (git-setup-diff-buffer
(apply #'git-run-command-buffer "*git-diff*" "diff-index" "-p" "-M" "HEAD" "--" (git-get-filenames files))))) (apply #'git-run-command-buffer "*git-diff*" "diff-index" "-p" "-M" "HEAD" "--" (git-get-filenames files)))))


(defun git-diff-file-merge-head (arg)
"Diff the marked file(s) against the first merge head (or the nth one with a numeric prefix)."
(interactive "p")
(let ((files (git-marked-files))
(merge-heads (git-get-merge-heads)))
(unless merge-heads (error "No merge in progress"))
(git-setup-diff-buffer
(apply #'git-run-command-buffer "*git-diff*" "diff-index" "-p" "-M"
(or (nth (1- arg) merge-heads) "HEAD") "--" (git-get-filenames files)))))

(defun git-diff-unmerged-file (stage) (defun git-diff-unmerged-file (stage)
"Diff the marked unmerged file(s) against the specified stage." "Diff the marked unmerged file(s) against the specified stage."
(let ((files (git-marked-files))) (let ((files (git-marked-files)))
@ -955,6 +969,7 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add
(define-key diff-map "=" 'git-diff-file) (define-key diff-map "=" 'git-diff-file)
(define-key diff-map "e" 'git-diff-file-idiff) (define-key diff-map "e" 'git-diff-file-idiff)
(define-key diff-map "E" 'git-find-file-imerge) (define-key diff-map "E" 'git-find-file-imerge)
(define-key diff-map "h" 'git-diff-file-merge-head)
(define-key diff-map "m" 'git-diff-file-mine) (define-key diff-map "m" 'git-diff-file-mine)
(define-key diff-map "o" 'git-diff-file-other) (define-key diff-map "o" 'git-diff-file-other)
(setq git-status-mode-map map))) (setq git-status-mode-map map)))

View File

@ -41,8 +41,12 @@ whatchanged
EOF EOF
while read cmd while read cmd
do do
sed -n "/NAME/,/git-$cmd/H; sed -n '
\$ {x; s/.*git-$cmd - \\(.*\\)/ {\"$cmd\", \"\1\"},/; p}" \ /NAME/,/git-'"$cmd"'/H
"Documentation/git-$cmd.txt" ${
x
s/.*git-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
p
}' "Documentation/git-$cmd.txt"
done done
echo "};" echo "};"

View File

@ -11,11 +11,15 @@ LF='
' '


all_strategies='recursive octopus resolve stupid ours' all_strategies='recursive octopus resolve stupid ours'
default_strategies='recursive' default_twohead_strategies='recursive'
default_octopus_strategies='octopus'
no_trivial_merge_strategies='ours'
use_strategies= use_strategies=

index_merge=t
if test "@@NO_PYTHON@@"; then if test "@@NO_PYTHON@@"; then
all_strategies='resolve octopus stupid ours' all_strategies='resolve octopus stupid ours'
default_strategies='resolve' default_twohead_strategies='resolve'
fi fi


dropsave() { dropsave() {
@ -90,8 +94,6 @@ do
shift shift
done done


test "$#" -le 2 && usage ;# we need at least two heads.

merge_msg="$1" merge_msg="$1"
shift shift
head_arg="$1" head_arg="$1"
@ -99,6 +101,8 @@ head=$(git-rev-parse --verify "$1"^0) || usage
shift shift


# All the rest are remote heads # All the rest are remote heads
test "$#" = 0 && usage ;# we need at least one remote head.

remoteheads= remoteheads=
for remote for remote
do do
@ -108,6 +112,27 @@ do
done done
set x $remoteheads ; shift set x $remoteheads ; shift


case "$use_strategies" in
'')
case "$#" in
1)
use_strategies="$default_twohead_strategies" ;;
*)
use_strategies="$default_octopus_strategies" ;;
esac
;;
esac

for s in $use_strategies
do
case " $s " in
*" $no_trivial_merge_strategies "*)
index_merge=f
break
;;
esac
done

case "$#" in case "$#" in
1) 1)
common=$(git-merge-base --all $head "$@") common=$(git-merge-base --all $head "$@")
@ -118,18 +143,21 @@ case "$#" in
esac esac
echo "$head" >"$GIT_DIR/ORIG_HEAD" echo "$head" >"$GIT_DIR/ORIG_HEAD"


case "$#,$common,$no_commit" in case "$index_merge,$#,$common,$no_commit" in
*,'',*) f,*)
# We've been told not to try anything clever. Skip to real merge.
;;
?,*,'',*)
# No common ancestors found. We need a real merge. # No common ancestors found. We need a real merge.
;; ;;
1,"$1",*) ?,1,"$1",*)
# If head can reach all the merge then we are up to date. # If head can reach all the merge then we are up to date.
# but first the most common case of merging one remote # but first the most common case of merging one remote.
echo "Already up-to-date." echo "Already up-to-date."
dropsave dropsave
exit 0 exit 0
;; ;;
1,"$head",*) ?,1,"$head",*)
# Again the most common case of merging one remote. # Again the most common case of merging one remote.
echo "Updating from $head to $1" echo "Updating from $head to $1"
git-update-index --refresh 2>/dev/null git-update-index --refresh 2>/dev/null
@ -139,11 +167,11 @@ case "$#,$common,$no_commit" in
dropsave dropsave
exit 0 exit 0
;; ;;
1,?*"$LF"?*,*) ?,1,?*"$LF"?*,*)
# We are not doing octopus and not fast forward. Need a # We are not doing octopus and not fast forward. Need a
# real merge. # real merge.
;; ;;
1,*,) ?,1,*,)
# We are not doing octopus, not fast forward, and have only # We are not doing octopus, not fast forward, and have only
# one common. See if it is really trivial. # one common. See if it is really trivial.
git var GIT_COMMITTER_IDENT >/dev/null || exit git var GIT_COMMITTER_IDENT >/dev/null || exit
@ -188,17 +216,6 @@ esac
# We are going to make a new commit. # We are going to make a new commit.
git var GIT_COMMITTER_IDENT >/dev/null || exit git var GIT_COMMITTER_IDENT >/dev/null || exit


case "$use_strategies" in
'')
case "$#" in
1)
use_strategies="$default_strategies" ;;
*)
use_strategies=octopus ;;
esac
;;
esac

# At this point, we need a real merge. No matter what strategy # At this point, we need a real merge. No matter what strategy
# we use, it would operate on the index, possibly affecting the # we use, it would operate on the index, possibly affecting the
# working tree, and when resolved cleanly, have the desired tree # working tree, and when resolved cleanly, have the desired tree
@ -270,11 +287,7 @@ done
# auto resolved the merge cleanly. # auto resolved the merge cleanly.
if test '' != "$result_tree" if test '' != "$result_tree"
then then
parents="-p $head" parents=$(git-show-branch --independent "$head" "$@" | sed -e 's/^/-p /')
for remote
do
parents="$parents -p $remote"
done
result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree $parents) || exit result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree $parents) || exit
finish "$result_commit" "Merge $result_commit, made by $wt_strategy." finish "$result_commit" "Merge $result_commit, made by $wt_strategy."
dropsave dropsave

View File

@ -973,6 +973,16 @@ static void *unpack_delta_entry(unsigned char *base_sha1,


if (left < 20) if (left < 20)
die("truncated pack file"); die("truncated pack file");

/* The base entry _must_ be in the same pack */
if (!find_pack_entry_one(base_sha1, &base_ent, p))
die("failed to find delta-pack base object %s",
sha1_to_hex(base_sha1));
base = unpack_entry_gently(&base_ent, type, &base_size);
if (!base)
die("failed to read delta-pack base object %s",
sha1_to_hex(base_sha1));

data = base_sha1 + 20; data = base_sha1 + 20;
data_size = left - 20; data_size = left - 20;
delta_data = xmalloc(delta_size); delta_data = xmalloc(delta_size);
@ -990,14 +1000,6 @@ static void *unpack_delta_entry(unsigned char *base_sha1,
if ((st != Z_STREAM_END) || stream.total_out != delta_size) if ((st != Z_STREAM_END) || stream.total_out != delta_size)
die("delta data unpack failed"); die("delta data unpack failed");


/* The base entry _must_ be in the same pack */
if (!find_pack_entry_one(base_sha1, &base_ent, p))
die("failed to find delta-pack base object %s",
sha1_to_hex(base_sha1));
base = unpack_entry_gently(&base_ent, type, &base_size);
if (!base)
die("failed to read delta-pack base object %s",
sha1_to_hex(base_sha1));
result = patch_delta(base, base_size, result = patch_delta(base, base_size,
delta_data, delta_size, delta_data, delta_size,
&result_size); &result_size);