From 52d59cc6452ed1aeec91f4c168a853ea8d9d5496 Mon Sep 17 00:00:00 2001 From: Sahil Dua Date: Sun, 18 Jun 2017 23:19:16 +0200 Subject: branch: add a --copy (-c) option to go with --move (-m) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the ability to --copy a branch and its reflog and configuration, this uses the same underlying machinery as the --move (-m) option except the reflog and configuration is copied instead of being moved. This is useful for e.g. copying a topic branch to a new version, e.g. work to work-2 after submitting the work topic to the list, while preserving all the tracking info and other configuration that goes with the branch, and unlike --move keeping the other already-submitted branch around for reference. Like --move, when the source branch is the currently checked out branch the HEAD is moved to the destination branch. In the case of --move we don't really have a choice (other than remaining on a detached HEAD) and in order to keep the functionality consistent, we are doing it in similar way for --copy too. The most common usage of this feature is expected to be moving to a new topic branch which is a copy of the current one, in that case moving to the target branch is what the user wants, and doesn't unexpectedly behave differently than --move would. One outstanding caveat of this implementation is that: git checkout maint && git checkout master && git branch -c topic && git checkout - Will check out 'maint' instead of 'master'. This is because the @{-N} feature (or its -1 shorthand "-") relies on HEAD reflogs created by the checkout command, so in this case we'll checkout maint instead of master, as the user might expect. What to do about that is left to a future change. Helped-by: Ævar Arnfjörð Bjarmason Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Sahil Dua Signed-off-by: Junio C Hamano --- refs/files-backend.c | 46 ++++++++++++++++++++++++++++++++++++++-------- refs/refs-internal.h | 4 ++++ 2 files changed, 42 insertions(+), 8 deletions(-) (limited to 'refs') diff --git a/refs/files-backend.c b/refs/files-backend.c index d8b3f73147..fcaf4f7191 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1733,9 +1733,9 @@ static int commit_ref_update(struct files_ref_store *refs, const struct object_id *oid, const char *logmsg, struct strbuf *err); -static int files_rename_ref(struct ref_store *ref_store, +static int files_copy_or_rename_ref(struct ref_store *ref_store, const char *oldrefname, const char *newrefname, - const char *logmsg) + const char *logmsg, int copy) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, "rename_ref"); @@ -1767,8 +1767,12 @@ static int files_rename_ref(struct ref_store *ref_store, } if (flag & REF_ISSYMREF) { - ret = error("refname %s is a symbolic ref, renaming it is not supported", - oldrefname); + if (copy) + ret = error("refname %s is a symbolic ref, copying it is not supported", + oldrefname); + else + ret = error("refname %s is a symbolic ref, renaming it is not supported", + oldrefname); goto out; } if (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) { @@ -1776,13 +1780,19 @@ static int files_rename_ref(struct ref_store *ref_store, goto out; } - if (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) { + if (!copy && log && rename(sb_oldref.buf, tmp_renamed_log.buf)) { ret = error("unable to move logfile logs/%s to logs/"TMP_RENAMED_LOG": %s", oldrefname, strerror(errno)); goto out; } - if (refs_delete_ref(&refs->base, logmsg, oldrefname, + if (copy && log && copy_file(tmp_renamed_log.buf, sb_oldref.buf, 0644)) { + ret = error("unable to copy logfile logs/%s to logs/"TMP_RENAMED_LOG": %s", + oldrefname, strerror(errno)); + goto out; + } + + if (!copy && refs_delete_ref(&refs->base, logmsg, oldrefname, orig_oid.hash, REF_NODEREF)) { error("unable to delete old %s", oldrefname); goto rollback; @@ -1795,7 +1805,7 @@ static int files_rename_ref(struct ref_store *ref_store, * the safety anyway; we want to delete the reference whatever * its current value. */ - if (!refs_read_ref_full(&refs->base, newrefname, + if (!copy && !refs_read_ref_full(&refs->base, newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, oid.hash, NULL) && refs_delete_ref(&refs->base, NULL, newrefname, @@ -1826,7 +1836,10 @@ static int files_rename_ref(struct ref_store *ref_store, lock = lock_ref_sha1_basic(refs, newrefname, NULL, NULL, NULL, REF_NODEREF, NULL, &err); if (!lock) { - error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf); + if (copy) + error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf); + else + error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf); strbuf_release(&err); goto rollback; } @@ -1877,6 +1890,22 @@ static int files_rename_ref(struct ref_store *ref_store, return ret; } +static int files_rename_ref(struct ref_store *ref_store, + const char *oldrefname, const char *newrefname, + const char *logmsg) +{ + return files_copy_or_rename_ref(ref_store, oldrefname, + newrefname, logmsg, 0); +} + +static int files_copy_ref(struct ref_store *ref_store, + const char *oldrefname, const char *newrefname, + const char *logmsg) +{ + return files_copy_or_rename_ref(ref_store, oldrefname, + newrefname, logmsg, 1); +} + static int close_ref(struct ref_lock *lock) { if (close_lock_file(lock->lk)) @@ -3383,6 +3412,7 @@ struct ref_storage_be refs_be_files = { files_create_symref, files_delete_refs, files_rename_ref, + files_copy_ref, files_ref_iterator_begin, files_read_raw_ref, diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 192f9f85c9..3c51194edb 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -543,6 +543,9 @@ typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg, typedef int rename_ref_fn(struct ref_store *ref_store, const char *oldref, const char *newref, const char *logmsg); +typedef int copy_ref_fn(struct ref_store *ref_store, + const char *oldref, const char *newref, + const char *logmsg); /* * Iterate over the references in `ref_store` whose names start with @@ -641,6 +644,7 @@ struct ref_storage_be { create_symref_fn *create_symref; delete_refs_fn *delete_refs; rename_ref_fn *rename_ref; + copy_ref_fn *copy_ref; ref_iterator_begin_fn *iterator_begin; read_raw_ref_fn *read_raw_ref; -- cgit v1.2.3