Merge branch 'cc/replace-with-the-same-type'

* cc/replace-with-the-same-type:
  Doc: 'replace' merge and non-merge commits
  t6050-replace: use some long option names
  replace: allow long option names
  Documentation/replace: add Creating Replacement Objects section
  t6050-replace: add test to clean up all the replace refs
  t6050-replace: test that objects are of the same type
  Documentation/replace: state that objects must be of the same type
  replace: forbid replacing an object with one of a different type
maint
Jonathan Nieder 2013-09-24 23:35:24 -07:00
commit 80f165a58a
3 changed files with 61 additions and 9 deletions

View File

@ -20,8 +20,14 @@ The name of the 'replace' reference is the SHA-1 of the object that is
replaced. The content of the 'replace' reference is the SHA-1 of the replaced. The content of the 'replace' reference is the SHA-1 of the
replacement object. replacement object.


The replaced object and the replacement object must be of the same type.
This restriction can be bypassed using `-f`.

Unless `-f` is given, the 'replace' reference must not yet exist. Unless `-f` is given, the 'replace' reference must not yet exist.


There is no other restriction on the replaced and replacement objects.
Merge commits can be replaced by non-merge commits and vice versa.

Replacement references will be used by default by all Git commands Replacement references will be used by default by all Git commands
except those doing reachability traversal (prune, pack transfer and except those doing reachability traversal (prune, pack transfer and
fsck). fsck).
@ -49,18 +55,34 @@ achieve the same effect as the `--no-replace-objects` option.
OPTIONS OPTIONS
------- -------
-f:: -f::
--force::
If an existing replace ref for the same object exists, it will If an existing replace ref for the same object exists, it will
be overwritten (instead of failing). be overwritten (instead of failing).


-d:: -d::
--delete::
Delete existing replace refs for the given objects. Delete existing replace refs for the given objects.


-l <pattern>:: -l <pattern>::
--list <pattern>::
List replace refs for objects that match the given pattern (or List replace refs for objects that match the given pattern (or
all if no pattern is given). all if no pattern is given).
Typing "git replace" without arguments, also lists all replace Typing "git replace" without arguments, also lists all replace
refs. refs.


CREATING REPLACEMENT OBJECTS
----------------------------

linkgit:git-filter-branch[1], linkgit:git-hash-object[1] and
linkgit:git-rebase[1], among other git commands, can be used to create
replacement objects from existing objects.

If you want to replace many blobs, trees or commits that are part of a
string of commits, you may just want to create a replacement string of
commits and then only replace the commit at the tip of the target
string of commits with the commit at the tip of the replacement string
of commits.

BUGS BUGS
---- ----
Comparing blobs or trees that have been replaced with those that Comparing blobs or trees that have been replaced with those that
@ -69,12 +91,13 @@ go back to a replaced commit will move the branch to the replacement
commit instead of the replaced commit. commit instead of the replaced commit.


There may be other problems when using 'git rev-list' related to There may be other problems when using 'git rev-list' related to
pending objects. And of course things may break if an object of one pending objects.
type is replaced by an object of another type (for example a blob
replaced by a commit).


SEE ALSO SEE ALSO
-------- --------
linkgit:git-hash-object[1]
linkgit:git-filter-branch[1]
linkgit:git-rebase[1]
linkgit:git-tag[1] linkgit:git-tag[1]
linkgit:git-branch[1] linkgit:git-branch[1]
linkgit:git[1] linkgit:git[1]

View File

@ -85,6 +85,7 @@ static int replace_object(const char *object_ref, const char *replace_ref,
int force) int force)
{ {
unsigned char object[20], prev[20], repl[20]; unsigned char object[20], prev[20], repl[20];
enum object_type obj_type, repl_type;
char ref[PATH_MAX]; char ref[PATH_MAX];
struct ref_lock *lock; struct ref_lock *lock;


@ -100,6 +101,15 @@ static int replace_object(const char *object_ref, const char *replace_ref,
if (check_refname_format(ref, 0)) if (check_refname_format(ref, 0))
die("'%s' is not a valid ref name.", ref); die("'%s' is not a valid ref name.", ref);


obj_type = sha1_object_info(object, NULL);
repl_type = sha1_object_info(repl, NULL);
if (!force && obj_type != repl_type)
die("Objects must be of the same type.\n"
"'%s' points to a replaced object of type '%s'\n"
"while '%s' points to a replacement object of type '%s'.",
object_ref, typename(obj_type),
replace_ref, typename(repl_type));

if (read_ref(ref, prev)) if (read_ref(ref, prev))
hashclr(prev); hashclr(prev);
else if (!force) else if (!force)
@ -118,9 +128,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
{ {
int list = 0, delete = 0, force = 0; int list = 0, delete = 0, force = 0;
struct option options[] = { struct option options[] = {
OPT_BOOL('l', NULL, &list, N_("list replace refs")), OPT_BOOL('l', "list", &list, N_("list replace refs")),
OPT_BOOL('d', NULL, &delete, N_("delete replace refs")), OPT_BOOL('d', "delete", &delete, N_("delete replace refs")),
OPT_BOOL('f', NULL, &force, N_("replace the ref if it exists")), OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
OPT_END() OPT_END()
}; };



View File

@ -122,9 +122,9 @@ test_expect_success '"git replace" listing and deleting' '
test "$HASH2" = "$(git replace -l)" && test "$HASH2" = "$(git replace -l)" &&
test "$HASH2" = "$(git replace)" && test "$HASH2" = "$(git replace)" &&
aa=${HASH2%??????????????????????????????????????} && aa=${HASH2%??????????????????????????????????????} &&
test "$HASH2" = "$(git replace -l "$aa*")" && test "$HASH2" = "$(git replace --list "$aa*")" &&
test_must_fail git replace -d $R && test_must_fail git replace -d $R &&
test_must_fail git replace -d && test_must_fail git replace --delete &&
test_must_fail git replace -l -d $HASH2 && test_must_fail git replace -l -d $HASH2 &&
git replace -d $HASH2 && git replace -d $HASH2 &&
git show $HASH2 | grep "A U Thor" && git show $HASH2 | grep "A U Thor" &&
@ -147,7 +147,7 @@ test_expect_success '"git replace" resolves sha1' '
git show $HASH2 | grep "O Thor" && git show $HASH2 | grep "O Thor" &&
test_must_fail git replace $HASH2 $R && test_must_fail git replace $HASH2 $R &&
git replace -f $HASH2 $R && git replace -f $HASH2 $R &&
test_must_fail git replace -f && test_must_fail git replace --force &&
test "$HASH2" = "$(git replace)" test "$HASH2" = "$(git replace)"
' '


@ -263,4 +263,23 @@ test_expect_success 'not just commits' '
test_cmp file.replaced file test_cmp file.replaced file
' '


test_expect_success 'replaced and replacement objects must be of the same type' '
test_must_fail git replace mytag $HASH1 &&
test_must_fail git replace HEAD^{tree} HEAD~1 &&
BLOB=$(git rev-parse :file) &&
test_must_fail git replace HEAD^ $BLOB
'

test_expect_success '-f option bypasses the type check' '
git replace -f mytag $HASH1 &&
git replace --force HEAD^{tree} HEAD~1 &&
git replace -f HEAD^ $BLOB
'

test_expect_success 'replace ref cleanup' '
test -n "$(git replace)" &&
git replace -d $(git replace) &&
test -z "$(git replace)"
'

test_done test_done