* [PATCH v4 11/23] log_ref_write(): inline function
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
This function doesn't do anything beyond call files_log_ref_write(), so
replace it with the latter at its call sites.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 24 ++++++++++--------------
1 file changed, 10 insertions(+), 14 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 49a119c..fd8a751 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2832,14 +2832,6 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
return 0;
}
-static int log_ref_write(const char *refname, const unsigned char *old_sha1,
- const unsigned char *new_sha1, const char *msg,
- int flags, struct strbuf *err)
-{
- return files_log_ref_write(refname, old_sha1, new_sha1, msg, flags,
- err);
-}
-
int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
const unsigned char *new_sha1, const char *msg,
int flags, struct strbuf *err)
@@ -2903,7 +2895,8 @@ static int commit_ref_update(struct files_ref_store *refs,
assert_main_repository(&refs->base, "commit_ref_update");
clear_loose_ref_cache(refs);
- if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {
+ if (files_log_ref_write(lock->ref_name, lock->old_oid.hash, sha1,
+ logmsg, 0, err)) {
char *old_msg = strbuf_detach(err, NULL);
strbuf_addf(err, "cannot update the ref '%s': %s",
lock->ref_name, old_msg);
@@ -2934,7 +2927,7 @@ static int commit_ref_update(struct files_ref_store *refs,
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name)) {
struct strbuf log_err = STRBUF_INIT;
- if (log_ref_write("HEAD", lock->old_oid.hash, sha1,
+ if (files_log_ref_write("HEAD", lock->old_oid.hash, sha1,
logmsg, 0, &log_err)) {
error("%s", log_err.buf);
strbuf_release(&log_err);
@@ -2973,7 +2966,8 @@ static void update_symref_reflog(struct ref_lock *lock, const char *refname,
struct strbuf err = STRBUF_INIT;
unsigned char new_sha1[20];
if (logmsg && !read_ref(target, new_sha1) &&
- log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) {
+ files_log_ref_write(refname, lock->old_oid.hash, new_sha1,
+ logmsg, 0, &err)) {
error("%s", err.buf);
strbuf_release(&err);
}
@@ -3748,9 +3742,11 @@ static int files_transaction_commit(struct ref_store *ref_store,
if (update->flags & REF_NEEDS_COMMIT ||
update->flags & REF_LOG_ONLY) {
- if (log_ref_write(lock->ref_name, lock->old_oid.hash,
- update->new_sha1,
- update->msg, update->flags, err)) {
+ if (files_log_ref_write(lock->ref_name,
+ lock->old_oid.hash,
+ update->new_sha1,
+ update->msg, update->flags,
+ err)) {
char *old_msg = strbuf_detach(err, NULL);
strbuf_addf(err, "cannot update the ref '%s': %s",
--
2.9.3
^ permalink raw reply related
* [PATCH v4 23/23] files_transaction_commit(): clean up empty directories
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
When deleting/pruning references, remove any directories that are made
empty by the deletion of loose references or of reflogs. Otherwise such
empty directories can survive forever and accumulate over time. (Even
'pack-refs', which is smart enough to remove the parent directories of
loose references that it prunes, leaves directories that were already
empty.)
And now that files_transaction_commit() takes care of deleting the
parent directories of loose references that it prunes, we don't have to
do that in prune_ref() anymore.
This change would be unwise if the *creation* of these directories could
race with our deletion of them. But the earlier changes in this patch
series made the creation paths robust against races, so now it is safe
to tidy them up more aggressively.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 34 ++++++++++++++++++++++++++++------
refs/refs-internal.h | 11 +++++++++--
t/t1400-update-ref.sh | 27 +++++++++++++++++++++++++++
3 files changed, 64 insertions(+), 8 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index bce0022..c426575 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2346,7 +2346,6 @@ static void prune_ref(struct ref_to_prune *r)
}
ref_transaction_free(transaction);
strbuf_release(&err);
- try_remove_empty_parents(r->name, REMOVE_EMPTY_PARENTS_REF);
}
static void prune_refs(struct ref_to_prune *r)
@@ -3794,6 +3793,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
+ update->flags |= REF_DELETED_LOOSE;
}
if (!(update->flags & REF_ISPRUNING))
@@ -3806,16 +3806,38 @@ static int files_transaction_commit(struct ref_store *ref_store,
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
- for_each_string_list_item(ref_to_delete, &refs_to_delete)
- unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
+
+ /* Delete the reflogs of any references that were deleted: */
+ for_each_string_list_item(ref_to_delete, &refs_to_delete) {
+ if (!unlink_or_warn(git_path("logs/%s", ref_to_delete->string)))
+ try_remove_empty_parents(ref_to_delete->string,
+ REMOVE_EMPTY_PARENTS_REFLOG);
+ }
+
clear_loose_ref_cache(refs);
cleanup:
transaction->state = REF_TRANSACTION_CLOSED;
- for (i = 0; i < transaction->nr; i++)
- if (transaction->updates[i]->backend_data)
- unlock_ref(transaction->updates[i]->backend_data);
+ for (i = 0; i < transaction->nr; i++) {
+ struct ref_update *update = transaction->updates[i];
+ struct ref_lock *lock = update->backend_data;
+
+ if (lock)
+ unlock_ref(lock);
+
+ if (update->flags & REF_DELETED_LOOSE) {
+ /*
+ * The loose reference was deleted. Delete any
+ * empty parent directories. (Note that this
+ * can only work because we have already
+ * removed the lockfile.)
+ */
+ try_remove_empty_parents(update->refname,
+ REMOVE_EMPTY_PARENTS_REF);
+ }
+ }
+
string_list_clear(&refs_to_delete, 0);
free(head_ref);
string_list_clear(&affected_refnames, 0);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index dc81acc..15d5a1e 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -56,6 +56,12 @@
#define REF_UPDATE_VIA_HEAD 0x100
/*
+ * Used as a flag in ref_update::flags when the loose reference has
+ * been deleted.
+ */
+#define REF_DELETED_LOOSE 0x200
+
+/*
* Return true iff refname is minimally safe. "Safe" here means that
* deleting a loose reference by this name will not do any damage, for
* example by causing a file that is not a reference to be deleted.
@@ -158,8 +164,9 @@ struct ref_update {
/*
* One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,
- * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY, and
- * REF_UPDATE_VIA_HEAD:
+ * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY,
+ * REF_UPDATE_VIA_HEAD, REF_NEEDS_COMMIT, and
+ * REF_DELETED_LOOSE:
*/
unsigned int flags;
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index d4fb977..97d8793 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -191,6 +191,33 @@ test_expect_success \
git update-ref HEAD'" $A &&
test $A"' = $(cat .git/'"$m"')'
+test_expect_success "empty directory removal" '
+ git branch d1/d2/r1 HEAD &&
+ git branch d1/r2 HEAD &&
+ test -f .git/refs/heads/d1/d2/r1 &&
+ test -f .git/logs/refs/heads/d1/d2/r1 &&
+ git branch -d d1/d2/r1 &&
+ ! test -e .git/refs/heads/d1/d2 &&
+ ! test -e .git/logs/refs/heads/d1/d2 &&
+ test -f .git/refs/heads/d1/r2 &&
+ test -f .git/logs/refs/heads/d1/r2
+'
+
+test_expect_success "symref empty directory removal" '
+ git branch e1/e2/r1 HEAD &&
+ git branch e1/r2 HEAD &&
+ git checkout e1/e2/r1 &&
+ test_when_finished "git checkout master" &&
+ test -f .git/refs/heads/e1/e2/r1 &&
+ test -f .git/logs/refs/heads/e1/e2/r1 &&
+ git update-ref -d HEAD &&
+ ! test -e .git/refs/heads/e1/e2 &&
+ ! test -e .git/logs/refs/heads/e1/e2 &&
+ test -f .git/refs/heads/e1/r2 &&
+ test -f .git/logs/refs/heads/e1/r2 &&
+ test -f .git/logs/HEAD
+'
+
cat >expect <<EOF
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch
--
2.9.3
^ permalink raw reply related
* [PATCH v4 18/23] delete_ref_loose(): derive loose reference path from lock
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
It is simpler to derive the path to the file that must be deleted from
"lock->ref_name" than from the lock_file object.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 39d6f5b..4d55364 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2430,10 +2430,7 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
* loose. The loose file name is the same as the
* lockfile name, minus ".lock":
*/
- char *loose_filename = get_locked_file_path(lock->lk);
- int res = unlink_or_msg(loose_filename, err);
- free(loose_filename);
- if (res)
+ if (unlink_or_msg(git_path("%s", lock->ref_name), err))
return 1;
}
return 0;
--
2.9.3
^ permalink raw reply related
* [PATCH v4 19/23] delete_ref_loose(): inline function
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
It was hardly doing anything anymore, and had only one caller.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 25 +++++++------------------
1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 4d55364..9abd7c3 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2421,21 +2421,6 @@ static int repack_without_refs(struct files_ref_store *refs,
return ret;
}
-static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
-{
- assert(err);
-
- if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
- /*
- * loose. The loose file name is the same as the
- * lockfile name, minus ".lock":
- */
- if (unlink_or_msg(git_path("%s", lock->ref_name), err))
- return 1;
- }
- return 0;
-}
-
static int files_delete_refs(struct ref_store *ref_store,
struct string_list *refnames, unsigned int flags)
{
@@ -3788,9 +3773,13 @@ static int files_transaction_commit(struct ref_store *ref_store,
if (update->flags & REF_DELETING &&
!(update->flags & REF_LOG_ONLY)) {
- if (delete_ref_loose(lock, update->type, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto cleanup;
+ if (!(update->type & REF_ISPACKED) ||
+ update->type & REF_ISSYMREF) {
+ /* It is a loose reference. */
+ if (unlink_or_msg(git_path("%s", lock->ref_name), err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
}
if (!(update->flags & REF_ISPRUNING))
--
2.9.3
^ permalink raw reply related
* [PATCH v4 22/23] try_remove_empty_parents(): teach to remove parents of reflogs, too
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
Add a new "flags" parameter that tells the function whether to remove
empty parent directories of the loose reference file, of the reflog
file, or both. The new functionality is not yet used.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 88f8c7a..bce0022 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2280,10 +2280,18 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
return 0;
}
+enum {
+ REMOVE_EMPTY_PARENTS_REF = 0x01,
+ REMOVE_EMPTY_PARENTS_REFLOG = 0x02
+};
+
/*
- * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Remove empty parent directories associated with the specified
+ * reference and/or its reflog, but spare [logs/]refs/ and immediate
+ * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or
+ * REMOVE_EMPTY_PARENTS_REFLOG.
*/
-static void try_remove_empty_parents(const char *refname)
+static void try_remove_empty_parents(const char *refname, unsigned int flags)
{
struct strbuf buf = STRBUF_INIT;
char *p, *q;
@@ -2299,7 +2307,7 @@ static void try_remove_empty_parents(const char *refname)
p++;
}
q = buf.buf + buf.len;
- while (1) {
+ while (flags & (REMOVE_EMPTY_PARENTS_REF | REMOVE_EMPTY_PARENTS_REFLOG)) {
while (q > p && *q != '/')
q--;
while (q > p && *(q-1) == '/')
@@ -2307,8 +2315,12 @@ static void try_remove_empty_parents(const char *refname)
if (q == p)
break;
strbuf_setlen(&buf, q - buf.buf);
- if (rmdir(git_path("%s", buf.buf)))
- break;
+ if ((flags & REMOVE_EMPTY_PARENTS_REF) &&
+ rmdir(git_path("%s", buf.buf)))
+ flags &= ~REMOVE_EMPTY_PARENTS_REF;
+ if ((flags & REMOVE_EMPTY_PARENTS_REFLOG) &&
+ rmdir(git_path("logs/%s", buf.buf)))
+ flags &= ~REMOVE_EMPTY_PARENTS_REFLOG;
}
strbuf_release(&buf);
}
@@ -2334,7 +2346,7 @@ static void prune_ref(struct ref_to_prune *r)
}
ref_transaction_free(transaction);
strbuf_release(&err);
- try_remove_empty_parents(r->name);
+ try_remove_empty_parents(r->name, REMOVE_EMPTY_PARENTS_REF);
}
static void prune_refs(struct ref_to_prune *r)
--
2.9.3
^ permalink raw reply related
* [PATCH v4 21/23] try_remove_empty_parents(): don't trash argument contents
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
It's bad manners and surprising and therefore error-prone.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 92a9d99..88f8c7a 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2282,13 +2282,15 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
/*
* Remove empty parents, but spare refs/ and immediate subdirs.
- * Note: munges *refname.
*/
-static void try_remove_empty_parents(char *refname)
+static void try_remove_empty_parents(const char *refname)
{
+ struct strbuf buf = STRBUF_INIT;
char *p, *q;
int i;
- p = refname;
+
+ strbuf_addstr(&buf, refname);
+ p = buf.buf;
for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
while (*p && *p != '/')
p++;
@@ -2296,8 +2298,7 @@ static void try_remove_empty_parents(char *refname)
while (*p == '/')
p++;
}
- for (q = p; *q; q++)
- ;
+ q = buf.buf + buf.len;
while (1) {
while (q > p && *q != '/')
q--;
@@ -2305,10 +2306,11 @@ static void try_remove_empty_parents(char *refname)
q--;
if (q == p)
break;
- *q = '\0';
- if (rmdir(git_path("%s", refname)))
+ strbuf_setlen(&buf, q - buf.buf);
+ if (rmdir(git_path("%s", buf.buf)))
break;
}
+ strbuf_release(&buf);
}
/* make sure nobody touched the ref, and unlink */
--
2.9.3
^ permalink raw reply related
* [PATCH v4 20/23] try_remove_empty_parents(): rename parameter "name" -> "refname"
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
This is the standard nomenclature.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 9abd7c3..92a9d99 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2282,13 +2282,13 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
/*
* Remove empty parents, but spare refs/ and immediate subdirs.
- * Note: munges *name.
+ * Note: munges *refname.
*/
-static void try_remove_empty_parents(char *name)
+static void try_remove_empty_parents(char *refname)
{
char *p, *q;
int i;
- p = name;
+ p = refname;
for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
while (*p && *p != '/')
p++;
@@ -2306,7 +2306,7 @@ static void try_remove_empty_parents(char *name)
if (q == p)
break;
*q = '\0';
- if (rmdir(git_path("%s", name)))
+ if (rmdir(git_path("%s", refname)))
break;
}
}
--
2.9.3
^ permalink raw reply related
* [PATCH v4 16/23] log_ref_setup(): manage the name of the reflog file internally
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
Instead of writing the name of the reflog file into a strbuf that is
supplied by the caller but not needed there, write it into a local
temporary buffer and remove the strbuf parameter entirely.
And while we're adjusting the function signature, reorder the arguments
to move the input parameters before the output parameters.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 69 ++++++++++++++++++++++++++--------------------------
1 file changed, 34 insertions(+), 35 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 9c5e804..846380f 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2719,37 +2719,36 @@ static int open_or_create_logfile(const char *path, void *cb)
}
/*
- * Create a reflog for a ref. Store its path to *logfile. If
- * force_create = 0, only create the reflog for certain refs (those
- * for which should_autocreate_reflog returns non-zero). Otherwise,
- * create it regardless of the reference name. If the logfile already
- * existed or was created, return 0 and set *logfd to the file
- * descriptor opened for appending to the file. If no logfile exists
- * and we decided not to create one, return 0 and set *logfd to -1. On
- * failure, fill in *err, set *logfd to -1, and return -1.
+ * Create a reflog for a ref. If force_create = 0, only create the
+ * reflog for certain refs (those for which should_autocreate_reflog
+ * returns non-zero). Otherwise, create it regardless of the reference
+ * name. If the logfile already existed or was created, return 0 and
+ * set *logfd to the file descriptor opened for appending to the file.
+ * If no logfile exists and we decided not to create one, return 0 and
+ * set *logfd to -1. On failure, fill in *err, set *logfd to -1, and
+ * return -1.
*/
-static int log_ref_setup(const char *refname,
- struct strbuf *logfile, int *logfd,
- struct strbuf *err, int force_create)
+static int log_ref_setup(const char *refname, int force_create,
+ int *logfd, struct strbuf *err)
{
- strbuf_git_path(logfile, "logs/%s", refname);
+ char *logfile = git_pathdup("logs/%s", refname);
if (force_create || should_autocreate_reflog(refname)) {
- if (raceproof_create_file(logfile->buf, open_or_create_logfile, logfd)) {
+ if (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {
if (errno == ENOENT)
strbuf_addf(err, "unable to create directory for '%s': "
- "%s", logfile->buf, strerror(errno));
+ "%s", logfile, strerror(errno));
else if (errno == EISDIR)
strbuf_addf(err, "there are still logs under '%s'",
- logfile->buf);
+ logfile);
else
strbuf_addf(err, "unable to append to '%s': %s",
- logfile->buf, strerror(errno));
+ logfile, strerror(errno));
- return -1;
+ goto error;
}
} else {
- *logfd = open(logfile->buf, O_APPEND | O_WRONLY, 0666);
+ *logfd = open(logfile, O_APPEND | O_WRONLY, 0666);
if (*logfd < 0) {
if (errno == ENOENT || errno == EISDIR) {
/*
@@ -2761,34 +2760,39 @@ static int log_ref_setup(const char *refname,
;
} else {
strbuf_addf(err, "unable to append to '%s': %s",
- logfile->buf, strerror(errno));
- return -1;
+ logfile, strerror(errno));
+ goto error;
}
}
}
if (*logfd >= 0)
- adjust_shared_perm(logfile->buf);
+ adjust_shared_perm(logfile);
+ free(logfile);
return 0;
+
+error:
+ free(logfile);
+ return -1;
}
static int files_create_reflog(struct ref_store *ref_store,
const char *refname, int force_create,
struct strbuf *err)
{
- int ret;
- struct strbuf sb = STRBUF_INIT;
int fd;
/* Check validity (but we don't need the result): */
files_downcast(ref_store, 0, "create_reflog");
- ret = log_ref_setup(refname, &sb, &fd, err, force_create);
+ if (log_ref_setup(refname, force_create, &fd, err))
+ return -1;
+
if (fd >= 0)
close(fd);
- strbuf_release(&sb);
- return ret;
+
+ return 0;
}
static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
@@ -2819,16 +2823,15 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
const unsigned char *new_sha1, const char *msg,
- struct strbuf *logfile, int flags,
- struct strbuf *err)
+ int flags, struct strbuf *err)
{
int logfd, result;
if (log_all_ref_updates < 0)
log_all_ref_updates = !is_bare_repository();
- result = log_ref_setup(refname, logfile, &logfd, err,
- flags & REF_FORCE_CREATE_REFLOG);
+ result = log_ref_setup(refname, flags & REF_FORCE_CREATE_REFLOG,
+ &logfd, err);
if (result)
return result;
@@ -2859,11 +2862,7 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
const unsigned char *new_sha1, const char *msg,
int flags, struct strbuf *err)
{
- struct strbuf sb = STRBUF_INIT;
- int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,
- err);
- strbuf_release(&sb);
- return ret;
+ return log_ref_write_1(refname, old_sha1, new_sha1, msg, flags, err);
}
/*
--
2.9.3
^ permalink raw reply related
* [PATCH v4 12/23] log_ref_setup(): separate code for create vs non-create
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
The behavior of this function (especially how it handles errors) is
quite different depending on whether we are willing to create the reflog
vs. whether we are only trying to open an existing reflog. So separate
the code paths.
This also simplifies the next steps.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 59 ++++++++++++++++++++++++++++++++++------------------
1 file changed, 39 insertions(+), 20 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index fd8a751..c8f6d82 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2718,45 +2718,64 @@ static int commit_ref(struct ref_lock *lock)
*/
static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)
{
- int logfd, oflags = O_APPEND | O_WRONLY;
+ int logfd;
strbuf_git_path(logfile, "logs/%s", refname);
+
if (force_create || should_autocreate_reflog(refname)) {
if (safe_create_leading_directories(logfile->buf) < 0) {
strbuf_addf(err, "unable to create directory for '%s': "
"%s", logfile->buf, strerror(errno));
return -1;
}
- oflags |= O_CREAT;
- }
-
- logfd = open(logfile->buf, oflags, 0666);
- if (logfd < 0) {
- if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
- return 0;
+ logfd = open(logfile->buf, O_APPEND | O_WRONLY | O_CREAT, 0666);
+ if (logfd < 0) {
+ if (errno == EISDIR) {
+ /*
+ * The directory that is in the way might be
+ * empty. Try to remove it.
+ */
+ if (remove_empty_directories(logfile)) {
+ strbuf_addf(err, "there are still logs under "
+ "'%s'", logfile->buf);
+ return -1;
+ }
+ logfd = open(logfile->buf, O_APPEND | O_WRONLY | O_CREAT, 0666);
+ }
- if (errno == EISDIR) {
- if (remove_empty_directories(logfile)) {
- strbuf_addf(err, "there are still logs under "
- "'%s'", logfile->buf);
+ if (logfd < 0) {
+ strbuf_addf(err, "unable to append to '%s': %s",
+ logfile->buf, strerror(errno));
return -1;
}
- logfd = open(logfile->buf, oflags, 0666);
}
-
+ } else {
+ logfd = open(logfile->buf, O_APPEND | O_WRONLY, 0666);
if (logfd < 0) {
- strbuf_addf(err, "unable to append to '%s': %s",
- logfile->buf, strerror(errno));
- return -1;
+ if (errno == ENOENT || errno == EISDIR) {
+ /*
+ * The logfile doesn't already exist,
+ * but that is not an error; it only
+ * means that we won't write log
+ * entries to it.
+ */
+ ;
+ } else {
+ strbuf_addf(err, "unable to append to '%s': %s",
+ logfile->buf, strerror(errno));
+ return -1;
+ }
}
}
- adjust_shared_perm(logfile->buf);
- close(logfd);
+ if (logfd >= 0) {
+ adjust_shared_perm(logfile->buf);
+ close(logfd);
+ }
+
return 0;
}
-
static int files_create_reflog(struct ref_store *ref_store,
const char *refname, int force_create,
struct strbuf *err)
--
2.9.3
^ permalink raw reply related
* [PATCH v4 15/23] log_ref_write_1(): don't depend on logfile argument
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
It's unnecessary to pass a strbuf holding the reflog path up and down
the call stack now that it is hardly needed by the callers. Remove the
places where log_ref_write_1() uses it, in preparation for making it
internal to log_ref_setup().
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index f723834..9c5e804 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2838,14 +2838,18 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
result = log_ref_write_fd(logfd, old_sha1, new_sha1,
git_committer_info(0), msg);
if (result) {
- strbuf_addf(err, "unable to append to '%s': %s", logfile->buf,
- strerror(errno));
+ int save_errno = errno;
+
+ strbuf_addf(err, "unable to append to '%s': %s",
+ git_path("logs/%s", refname), strerror(save_errno));
close(logfd);
return -1;
}
if (close(logfd)) {
- strbuf_addf(err, "unable to append to '%s': %s", logfile->buf,
- strerror(errno));
+ int save_errno = errno;
+
+ strbuf_addf(err, "unable to append to '%s': %s",
+ git_path("logs/%s", refname), strerror(save_errno));
return -1;
}
return 0;
--
2.9.3
^ permalink raw reply related
* [PATCH v4 17/23] log_ref_write_1(): inline function
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
Now files_log_ref_write() doesn't do anything beyond call
log_ref_write_1(), so inline the latter into the former.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 846380f..39d6f5b 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2821,9 +2821,9 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
return 0;
}
-static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
- const unsigned char *new_sha1, const char *msg,
- int flags, struct strbuf *err)
+int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
+ const unsigned char *new_sha1, const char *msg,
+ int flags, struct strbuf *err)
{
int logfd, result;
@@ -2858,13 +2858,6 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
return 0;
}
-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
- const unsigned char *new_sha1, const char *msg,
- int flags, struct strbuf *err)
-{
- return log_ref_write_1(refname, old_sha1, new_sha1, msg, flags, err);
-}
-
/*
* Write sha1 into the open lockfile, then close the lockfile. On
* errors, rollback the lockfile, fill in *err and
--
2.9.3
^ permalink raw reply related
* [PATCH v4 14/23] log_ref_setup(): pass the open file descriptor back to the caller
From: Michael Haggerty @ 2017-01-06 16:22 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, David Turner, Jacob Keller, Philip Oakley,
Michael Haggerty
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
This function will most often be called by log_ref_write_1(), which
wants to append to the reflog file. In that case, it is silly to close
the file only for the caller to reopen it immediately. So, in the case
that the file was opened, pass the open file descriptor back to the
caller.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 39 ++++++++++++++++++++++-----------------
1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 27d4fd3..f723834 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2719,19 +2719,23 @@ static int open_or_create_logfile(const char *path, void *cb)
}
/*
- * Create a reflog for a ref. If force_create = 0, the reflog will
- * only be created for certain refs (those for which
- * should_autocreate_reflog returns non-zero. Otherwise, create it
- * regardless of the ref name. Fill in *err and return -1 on failure.
+ * Create a reflog for a ref. Store its path to *logfile. If
+ * force_create = 0, only create the reflog for certain refs (those
+ * for which should_autocreate_reflog returns non-zero). Otherwise,
+ * create it regardless of the reference name. If the logfile already
+ * existed or was created, return 0 and set *logfd to the file
+ * descriptor opened for appending to the file. If no logfile exists
+ * and we decided not to create one, return 0 and set *logfd to -1. On
+ * failure, fill in *err, set *logfd to -1, and return -1.
*/
-static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)
+static int log_ref_setup(const char *refname,
+ struct strbuf *logfile, int *logfd,
+ struct strbuf *err, int force_create)
{
- int logfd;
-
strbuf_git_path(logfile, "logs/%s", refname);
if (force_create || should_autocreate_reflog(refname)) {
- if (raceproof_create_file(logfile->buf, open_or_create_logfile, &logfd)) {
+ if (raceproof_create_file(logfile->buf, open_or_create_logfile, logfd)) {
if (errno == ENOENT)
strbuf_addf(err, "unable to create directory for '%s': "
"%s", logfile->buf, strerror(errno));
@@ -2745,8 +2749,8 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct str
return -1;
}
} else {
- logfd = open(logfile->buf, O_APPEND | O_WRONLY, 0666);
- if (logfd < 0) {
+ *logfd = open(logfile->buf, O_APPEND | O_WRONLY, 0666);
+ if (*logfd < 0) {
if (errno == ENOENT || errno == EISDIR) {
/*
* The logfile doesn't already exist,
@@ -2763,10 +2767,8 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct str
}
}
- if (logfd >= 0) {
+ if (*logfd >= 0)
adjust_shared_perm(logfile->buf);
- close(logfd);
- }
return 0;
}
@@ -2777,11 +2779,14 @@ static int files_create_reflog(struct ref_store *ref_store,
{
int ret;
struct strbuf sb = STRBUF_INIT;
+ int fd;
/* Check validity (but we don't need the result): */
files_downcast(ref_store, 0, "create_reflog");
- ret = log_ref_setup(refname, &sb, err, force_create);
+ ret = log_ref_setup(refname, &sb, &fd, err, force_create);
+ if (fd >= 0)
+ close(fd);
strbuf_release(&sb);
return ret;
}
@@ -2817,17 +2822,17 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
struct strbuf *logfile, int flags,
struct strbuf *err)
{
- int logfd, result, oflags = O_APPEND | O_WRONLY;
+ int logfd, result;
if (log_all_ref_updates < 0)
log_all_ref_updates = !is_bare_repository();
- result = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);
+ result = log_ref_setup(refname, logfile, &logfd, err,
+ flags & REF_FORCE_CREATE_REFLOG);
if (result)
return result;
- logfd = open(logfile->buf, oflags);
if (logfd < 0)
return 0;
result = log_ref_write_fd(logfd, old_sha1, new_sha1,
--
2.9.3
^ permalink raw reply related
* git branch --editdescription fatal error
From: Jake Lambert @ 2017-01-06 17:25 UTC (permalink / raw)
To: git@vger.kernel.org
Hello,
When executing "git branch <branch> --edit-description" on a branch with no description set, I get "fatal: could not unset 'branch.<branch>.description". It would seem that the unsetting piece should occur only after checking if it was set in the first place. I am not too familiar with the inner workings of git, but if you accept pull requests, I'm happy to give it a shot if you can give me directions as to how to submit one to you.
While I have you, I would also like to make a feature request for branch descriptions to (optionally) be included in the output of git branch <noargs> (or something like git branch --descriptions). This is particularly helpful where branch names match JIRA tickets numbers or are simply so old I don't remember what they are but don't want to delete them just in case.
Thank you for maintaining this absolutely wonderful program, and have a happy New Year!
Best,
Jake
Jake Lambert
Full Stack Developer
The Company On A Hill
www.onahill.co
^ permalink raw reply
* Re: git branch --editdescription fatal error
From: Stefan Beller @ 2017-01-06 17:55 UTC (permalink / raw)
To: Jake Lambert; +Cc: git@vger.kernel.org
In-Reply-To: <MWHPR19MB11357994E6C43DBCC0931CD7BC630@MWHPR19MB1135.namprd19.prod.outlook.com>
On Fri, Jan 6, 2017 at 9:25 AM, Jake Lambert <jake@onahill.co> wrote:
> Hello,
>
> When executing "git branch <branch> --edit-description" on a branch with no description set, I get "fatal: could not unset 'branch.<branch>.description". It would seem that the unsetting piece should occur only after checking if it was set in the first place.
That seems strange. Is it possible that your config is not writable?
(.git/config, ~/gitconfig, you'd need to find out where the <branch>
is configured already via git config --global/--system/--local --list)
> I am not too familiar with the inner workings of git, but if you accept pull requests, I'm happy to give it a shot if you can give me directions as to how to submit one to you.
We rather do email based workflow, see
https://github.com/git/git/blob/master/Documentation/SubmittingPatches
(We use github only as a host, you could also obtain it from one of
https://git-blame.blogspot.com/p/git-public-repositories.html)
>
> While I have you, I would also like to make a feature request for branch descriptions to (optionally) be included in the output of git branch <noargs> (or something like git branch --descriptions). This is particularly helpful where branch names match JIRA tickets numbers or are simply so old I don't remember what they are but don't want to delete them just in case.
Apparently the branch descriptions are only used for the request-pull
subcommand,
https://github.com/git/git/commit/c0168147831fce00975949213eef3471b7a2b76b
and the merge message
https://github.com/git/git/commit/898eacd8ada2d012f977948350ed60845e238037
If you want to work on both of these issues, have a look at
edit_branch_description
as well as print_ref_list in builtin/branch.c
> Thank you for maintaining this absolutely wonderful program, and have a happy New Year!
Happy new year!
Stefan
^ permalink raw reply
* Rebasing a branch with merges
From: Robert Dailey @ 2017-01-06 19:12 UTC (permalink / raw)
To: Git
Here's the scenario:
I create a topic branch so one other developer and myself can work on
a feature that takes 2 weeks to complete. During that 2 week period,
changes are occurring on master that I need in my topic branch. Since
I have a collaborator on the branch, I opt for merges instead of
rebase.
Each day I merge from master to the topic branch, which changes code
I'm actively working in and requires semantic changes (functions
renamed, moved, etc).
Once I'm ready to merge the topic branch back into master, I have two
options (bearing in mind the goal is to keep history as clean as
possible. Furthermore this implies that the constant merging into
topic from master has made the topic branch look unwieldy and
difficult to audit):
1. Do a squash merge, which keeps history clean but we lose context
for the important bits (the commits representing units of work that
contribute to the topic itself).
2. Do a final rebase prior to merging.
#2 doesn't seem to be possible due to patch ordering. For example, if
I have real commits after merge commits that depend on those changes
from master being present as a base at that point in time, the rebase
will cause the patch before it to no longer include those changes from
master.
Is there a mechanism to rebase in this situation to both achieve a
clean, linear history for the topic branch and allow fast forward
merging if desired, while still not causing superfluous conflicts due
to the merges being omitted during the rebase?
Thanks in advance for any advice in this scenario.
^ permalink raw reply
* Re: git branch --editdescription fatal error
From: Ralf Thielow @ 2017-01-06 19:31 UTC (permalink / raw)
To: Stefan Beller; +Cc: Jake Lambert, git@vger.kernel.org
In-Reply-To: <CAGZ79kaz_hS9P7vqV9EhZazM_g0OUdRfMtFPJ7gFu-h-ku=NKw@mail.gmail.com>
2017-01-06 18:55 GMT+01:00 Stefan Beller <sbeller@google.com>:
> On Fri, Jan 6, 2017 at 9:25 AM, Jake Lambert <jake@onahill.co> wrote:
>> Hello,
>>
>> When executing "git branch <branch> --edit-description" on a branch with no description set, I get "fatal: could not unset 'branch.<branch>.description". It would seem that the unsetting piece should occur only after checking if it was set in the first place.
>
> That seems strange. Is it possible that your config is not writable?
> (.git/config, ~/gitconfig, you'd need to find out where the <branch>
> is configured already via git config --global/--system/--local --list)
>
Have you actually tried to reproduce this issue? I'm on current next
and can reproduce the problem.
It seems Git is not happy with unsetting non-existent configs. And at
least the following piece of code
seems to be there since 10bea152a (Add functions git_config_set() and
git_config_set_multivar(), 2005-11-17).
config.c:2212
/* if nothing to unset, or too many matches, error out */
if ((store.seen == 0 && value == NULL) ||
I wonder why it's an error to remove something that doesn't exist
except the case we 'know' it
exists because of further actions. I don't think this is a thing, though.
Just for the record: `git config --unset branch.$branch.description`
does return with code "5" here as well.
I think removing things that don't exist should behave the same like
removing things that do exist, with
a success.
Ralf
^ permalink raw reply
* Re: Regression: Ctrl-c from the pager in an alias exits it
From: Jeff King @ 2017-01-06 19:41 UTC (permalink / raw)
To: Johannes Sixt; +Cc: Trygve Aaberge, Nguyễn Thái Ngọc Duy, git
In-Reply-To: <3d433abf-71a2-4702-f62b-e254520dc32c@kdbg.org>
On Fri, Jan 06, 2017 at 03:39:59PM +0100, Johannes Sixt wrote:
> > diff --git a/run-command.c b/run-command.c
> > index ca905a9e80..db47c429b7 100644
> > --- a/run-command.c
> > +++ b/run-command.c
> > @@ -29,6 +29,8 @@ static int installed_child_cleanup_handler;
> >
> > static void cleanup_children(int sig, int in_signal)
> > {
> > + struct child_to_clean *children_to_wait_for = NULL;
> > +
> > while (children_to_clean) {
> > struct child_to_clean *p = children_to_clean;
> > children_to_clean = p->next;
> > @@ -45,6 +47,17 @@ static void cleanup_children(int sig, int in_signal)
> > }
> >
> > kill(p->pid, sig);
> > + p->next = children_to_wait_for;
> > + children_to_wait_for = p;
> > + }
> > +
> > + while (children_to_wait_for) {
> > + struct child_to_clean *p = children_to_wait_for;
> > + children_to_wait_for = p->next;
> > +
> > + while (waitpid(p->pid, NULL, 0) < 0 && errno == EINTR)
> > + ; /* spin waiting for process exit or error */
> > +
> > if (!in_signal)
> > free(p);
> > }
> >
>
> This looks like the minimal change necessary. I wonder, though, whether the
> new local variable is really required. Wouldn't it be sufficient to walk the
> children_to_clean chain twice?
Yeah, I considered that. The fact that we disassemble the list in the
first loop has two side effects:
1. It lets us free the list as we go (for the !in_signal case).
2. If we were to get another signal, it makes us sort-of reentrant. We
will only kill and wait for each pid once.
Obviously (1) moves down to the lower loop, but I was trying to preserve
(2). I'm not sure if it is worth bothering, though. The way we pull
items off of the list is certainly not atomic (it does shorten the race
to a few instructions, though, versus potentially waiting on waitpid()
to return).
My bigger concern with the whole thing is whether we could hit some sort
of deadlock if the child doesn't die when we send it a signal. E.g.,
imagine we have a pipe open to the child and somebody sends SIGTERM to
us. We propagate SIGTERM to the child, and then waitpid() for it. The
child decides to ignore our SIGTERM for some reason and keep reading
until EOF on the pipe. It won't ever get it, and the two processes will
hang forever.
You can argue perhaps that the child is broken in that case. And I doubt
this could trigger when running a git sub-command. But we may add more
children in the future. Right now we use it for the new multi-file
clean/smudge filters. They use the hook feature to close the
descriptors, but note that that won't run in the in_signal case.
So I dunno. Maybe this waiting should be restricted only to certain
cases like executing git sub-commands.
-Peff
^ permalink raw reply
* Re: git branch --editdescription fatal error
From: Stefan Beller @ 2017-01-06 19:43 UTC (permalink / raw)
To: Ralf Thielow; +Cc: Jake Lambert, git@vger.kernel.org
In-Reply-To: <CAN0XMO+zzYOXF2gwr=0Tu-7T5BH7f_L+FhfD8RUenDeq3xFHjA@mail.gmail.com>
On Fri, Jan 6, 2017 at 11:31 AM, Ralf Thielow <ralf.thielow@gmail.com> wrote:
> 2017-01-06 18:55 GMT+01:00 Stefan Beller <sbeller@google.com>:
>> On Fri, Jan 6, 2017 at 9:25 AM, Jake Lambert <jake@onahill.co> wrote:
>>> Hello,
>>>
>>> When executing "git branch <branch> --edit-description" on a branch with no description set, I get "fatal: could not unset 'branch.<branch>.description". It would seem that the unsetting piece should occur only after checking if it was set in the first place.
>>
>> That seems strange. Is it possible that your config is not writable?
>> (.git/config, ~/gitconfig, you'd need to find out where the <branch>
>> is configured already via git config --global/--system/--local --list)
>>
>
> Have you actually tried to reproduce this issue? I'm on current next
> and can reproduce the problem.
eh, I was on $random_version that I currently have installed
(with messed up submodule code, but otherwise close to master).
this gives hope that a bisect between master..next will give us a culprit?
>
> I think removing things that don't exist should behave the same like
> removing things that do exist, with
> a success.
I am not sure. Consider the tool "rm"
$ rm no_exist
rm: cannot remove ‘no_exist’: No such file or directory
$ echo $?
1
You have to use the --force.
For this specific use case I agree we should then set the force flag
to let the the removal of the config option succeed no matter if it
existed before.
Stefan
^ permalink raw reply
* Re: [PATCH v4 00/23] Delete directories left empty after ref deletion
From: Jeff King @ 2017-01-06 19:44 UTC (permalink / raw)
To: Michael Haggerty
Cc: Junio C Hamano, git, David Turner, Jacob Keller, Philip Oakley
In-Reply-To: <cover.1483719289.git.mhagger@alum.mit.edu>
On Fri, Jan 06, 2017 at 05:22:20PM +0100, Michael Haggerty wrote:
> This is v4 of this patch series. Thanks to Peff, Junio, Jake, and
> Philip for their feedback about v3. I believe I have addressed all of
> the comments about v1 [1], v2 [2], and v3 [3].
This version looks good to me.
-Peff
^ permalink raw reply
* Re: git branch --editdescription fatal error
From: Ralf Thielow @ 2017-01-06 20:05 UTC (permalink / raw)
To: Stefan Beller; +Cc: Jake Lambert, git@vger.kernel.org
In-Reply-To: <CAGZ79kZOfHWP_pQGN1QcmR71Ft6ib0aPwNKX80YMT7KcK0_Stg@mail.gmail.com>
2017-01-06 20:43 GMT+01:00 Stefan Beller <sbeller@google.com>:
> On Fri, Jan 6, 2017 at 11:31 AM, Ralf Thielow <ralf.thielow@gmail.com> wrote:
>> 2017-01-06 18:55 GMT+01:00 Stefan Beller <sbeller@google.com>:
>>> On Fri, Jan 6, 2017 at 9:25 AM, Jake Lambert <jake@onahill.co> wrote:
>>>> Hello,
>>>>
>>>> When executing "git branch <branch> --edit-description" on a branch with no description set, I get "fatal: could not unset 'branch.<branch>.description". It would seem that the unsetting piece should occur only after checking if it was set in the first place.
>>>
>>> That seems strange. Is it possible that your config is not writable?
>>> (.git/config, ~/gitconfig, you'd need to find out where the <branch>
>>> is configured already via git config --global/--system/--local --list)
>>>
>>
>> Have you actually tried to reproduce this issue? I'm on current next
>> and can reproduce the problem.
>
> eh, I was on $random_version that I currently have installed
> (with messed up submodule code, but otherwise close to master).
>
> this gives hope that a bisect between master..next will give us a culprit?
>
Hm.
I can confirm it appears on current master (e05806da9).
>>
>> I think removing things that don't exist should behave the same like
>> removing things that do exist, with
>> a success.
>
> I am not sure. Consider the tool "rm"
>
> $ rm no_exist
> rm: cannot remove ‘no_exist’: No such file or directory
> $ echo $?
> 1
>
> You have to use the --force.
>
> For this specific use case I agree we should then set the force flag
> to let the the removal of the config option succeed no matter if it
> existed before.
>
> Stefan
Can you think of a case where this flag should be set to false?
Ralf
^ permalink raw reply
* Re: git branch --editdescription fatal error
From: Jeff King @ 2017-01-06 20:10 UTC (permalink / raw)
To: Stefan Beller; +Cc: Ralf Thielow, Jake Lambert, git@vger.kernel.org
In-Reply-To: <CAGZ79kZOfHWP_pQGN1QcmR71Ft6ib0aPwNKX80YMT7KcK0_Stg@mail.gmail.com>
On Fri, Jan 06, 2017 at 11:43:52AM -0800, Stefan Beller wrote:
> >>> When executing "git branch <branch> --edit-description" on a branch with no description set, I get "fatal: could not unset 'branch.<branch>.description". It would seem that the unsetting piece should occur only after checking if it was set in the first place.
> >>
> >> That seems strange. Is it possible that your config is not writable?
> >> (.git/config, ~/gitconfig, you'd need to find out where the <branch>
> >> is configured already via git config --global/--system/--local --list)
> >>
> >
> > Have you actually tried to reproduce this issue? I'm on current next
> > and can reproduce the problem.
>
> eh, I was on $random_version that I currently have installed
> (with messed up submodule code, but otherwise close to master).
Hmm. I can reproduce, but only in one situation: when the new
description is empty. In which case we try to delete the variable. In
other words:
[this breaks; the file remains empty and we try to delete the
nonexistent config]
$ EDITOR=true git branch --edit-description master
fatal: could not unset 'branch.master.description'
[this works; we actually set the variable]
$ EDITOR='echo foo >' git branch --edit-description master
$ git config branch.master.description
foo
[and now the unsetting works; note we have to truncate here, since
the file will be prepopulated with "foo" from the existing desc]
$ EDITOR='>' git branch --edit-description master
$ git config branch.master.description
[no output]
The history of this behavior is a bit funny.
In old versions of git, we would return a failing exit code of "1" from
git-branch, with no message.
Then in bd25f89014 (branch: die on config error when editing branch
description, 2016-02-22), we actually started returning "0"! This was
because the config code did not propagate errors from its helper
functions in all cases.
That was fixed by 9c14bb08a4 (git_config_set_multivar_in_file: all
non-zero returns are errors, 2016-04-09), giving the behavior we see
today.
So between v2.7.3 and v2.8.3, we did return 0, but I think that was a
bug (we also returned 0 for a lot of other bogus cases, too).
I could see either behavior as reasonable, but I think the right
solution would be for the branch code from bd25f89014 to use the
"gently" function set the variable, and then decide which cases should
be silently ignored, and which propagated as errors.
-Peff
^ permalink raw reply
* [PATCH 0/5] refactor unpack-trees
From: Stefan Beller @ 2017-01-06 21:03 UTC (permalink / raw)
To: gitster, l.s.r; +Cc: git, Stefan Beller
In-Reply-To: <CAGZ79kaM=Uosm7KAvAP-8w59Tyfc7LZiV3ZOr=PZnBgMCzr2AA@mail.gmail.com>
unpack-trees is a central file needed for the understanding
of working tree manipulation. To help with the understanding
refactor the code to be more readable.
The first patch was a standalone patch 8 days ago;
now incorporated into this series as a v3,
reducing the scope of the checkout state.
The second patch removes a single continue statement;
it needed some digging to explain, but looks trivial.
The last 3 patches shorten the check_updates function by adding more
functions. If we ever want to parallelize file IO then these smaller
functions would be the scope to do it, keeping the check_updates as
a high level function guiding through the steps what is happening during
a working tree update.
Thanks,
Stefan
Stefan Beller (5):
unpack-trees: move checkout state into check_updates
unpack-trees: remove unneeded continue
unpack-trees: factor progress setup out of check_updates
unpack-trees: factor file removal out of check_updates
unpack-trees: factor working tree update out of check_updates
unpack-trees.c | 96 ++++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 64 insertions(+), 32 deletions(-)
--
2.11.0.31.g919a8d0.dirty
^ permalink raw reply
* [PATCHv3 1/5] unpack-trees: move checkout state into check_updates
From: Stefan Beller @ 2017-01-06 21:03 UTC (permalink / raw)
To: gitster, l.s.r; +Cc: git, Stefan Beller
In-Reply-To: <20170106210330.31761-1-sbeller@google.com>
The checkout state was introduced via 16da134b1f9
(read-trees: refactor the unpack_trees() part, 2006-07-30). An attempt to
refactor the checkout state was done in b56aa5b268e (unpack-trees: pass
checkout state explicitly to check_updates(), 2016-09-13), but we can
go even further.
The `struct checkout state` is not used in unpack_trees apart from
initializing it, so move it into the function that makes use of it,
which is `check_updates`.
Reviewed-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Stefan Beller <sbeller@google.com>
---
unpack-trees.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/unpack-trees.c b/unpack-trees.c
index bc56195e27..55c75b4d6a 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -237,77 +237,82 @@ static void display_error_msgs(struct unpack_trees_options *o)
}
/*
* Unlink the last component and schedule the leading directories for
* removal, such that empty directories get removed.
*/
static void unlink_entry(const struct cache_entry *ce)
{
if (!check_leading_path(ce->name, ce_namelen(ce)))
return;
if (remove_or_warn(ce->ce_mode, ce->name))
return;
schedule_dir_for_removal(ce->name, ce_namelen(ce));
}
-static int check_updates(struct unpack_trees_options *o,
- const struct checkout *state)
+static int check_updates(struct unpack_trees_options *o)
{
unsigned cnt = 0, total = 0;
+ int i, errs = 0;
+
struct progress *progress = NULL;
struct index_state *index = &o->result;
- int i;
- int errs = 0;
+ struct checkout state = CHECKOUT_INIT;
+
+ state.force = 1;
+ state.quiet = 1;
+ state.refresh_cache = 1;
+ state.istate = index;
if (o->update && o->verbose_update) {
for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
const struct cache_entry *ce = index->cache[cnt];
if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
total++;
}
progress = start_progress_delay(_("Checking out files"),
total, 50, 1);
cnt = 0;
}
if (o->update)
- git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result);
+ git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
for (i = 0; i < index->cache_nr; i++) {
const struct cache_entry *ce = index->cache[i];
if (ce->ce_flags & CE_WT_REMOVE) {
display_progress(progress, ++cnt);
if (o->update && !o->dry_run)
unlink_entry(ce);
continue;
}
}
- remove_marked_cache_entries(&o->result);
+ remove_marked_cache_entries(index);
remove_scheduled_dirs();
for (i = 0; i < index->cache_nr; i++) {
struct cache_entry *ce = index->cache[i];
if (ce->ce_flags & CE_UPDATE) {
if (ce->ce_flags & CE_WT_REMOVE)
die("BUG: both update and delete flags are set on %s",
ce->name);
display_progress(progress, ++cnt);
ce->ce_flags &= ~CE_UPDATE;
if (o->update && !o->dry_run) {
- errs |= checkout_entry(ce, state, NULL);
+ errs |= checkout_entry(ce, &state, NULL);
}
}
}
stop_progress(&progress);
if (o->update)
git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
return errs != 0;
}
static int verify_uptodate_sparse(const struct cache_entry *ce,
struct unpack_trees_options *o);
static int verify_absent_sparse(const struct cache_entry *ce,
enum unpack_trees_error_types,
struct unpack_trees_options *o);
@@ -1113,38 +1118,33 @@ static void mark_new_skip_worktree(struct exclude_list *el,
static int verify_absent(const struct cache_entry *,
enum unpack_trees_error_types,
struct unpack_trees_options *);
/*
* N-way merge "len" trees. Returns 0 on success, -1 on failure to manipulate the
* resulting index, -2 on failure to reflect the changes to the work tree.
*
* CE_ADDED, CE_UNPACKED and CE_NEW_SKIP_WORKTREE are used internally
*/
int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
{
int i, ret;
static struct cache_entry *dfc;
struct exclude_list el;
- struct checkout state = CHECKOUT_INIT;
if (len > MAX_UNPACK_TREES)
die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
- state.force = 1;
- state.quiet = 1;
- state.refresh_cache = 1;
- state.istate = &o->result;
memset(&el, 0, sizeof(el));
if (!core_apply_sparse_checkout || !o->update)
o->skip_sparse_checkout = 1;
if (!o->skip_sparse_checkout) {
char *sparse = git_pathdup("info/sparse-checkout");
if (add_excludes_from_file_to_list(sparse, "", 0, &el, 0) < 0)
o->skip_sparse_checkout = 1;
else
o->el = ⪙
free(sparse);
}
memset(&o->result, 0, sizeof(o->result));
o->result.initialized = 1;
@@ -1257,31 +1257,31 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
if (ret < 0)
goto return_failed;
/*
* Sparse checkout is meant to narrow down checkout area
* but it does not make sense to narrow down to empty working
* tree. This is usually a mistake in sparse checkout rules.
* Do not allow users to do that.
*/
if (o->result.cache_nr && empty_worktree) {
ret = unpack_failed(o, "Sparse checkout leaves no entry on working directory");
goto done;
}
}
o->src_index = NULL;
- ret = check_updates(o, &state) ? (-2) : 0;
+ ret = check_updates(o) ? (-2) : 0;
if (o->dst_index) {
if (!ret) {
if (!o->result.cache_tree)
o->result.cache_tree = cache_tree();
if (!cache_tree_fully_valid(o->result.cache_tree))
cache_tree_update(&o->result,
WRITE_TREE_SILENT |
WRITE_TREE_REPAIR);
}
discard_index(o->dst_index);
*o->dst_index = o->result;
} else {
discard_index(&o->result);
}
--
2.11.0.31.g919a8d0.dirty
^ permalink raw reply related
* Re: [PATCHv2] unpack-trees: move checkout state into check_updates
From: Stefan Beller @ 2017-01-06 21:03 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git@vger.kernel.org, René Scharfe
In-Reply-To: <xmqq8tqv6051.fsf@gitster.mtv.corp.google.com>
On Sat, Dec 31, 2016 at 5:26 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> Signed-off-by: Stefan Beller <sbeller@google.com>
>> ---
>
> I'd add René's Reviewed-by: here.
done
>
> I think moving heavier and initialized variables earlier and more
> lightweight and ephemeral ones like "i" later does make it easier to
> follow. "errs" has the significance and the lifetime similar to
> cnt/total, and logically should be higher, though. It is not a big
> enough deal to reroll (but as your futzing of the variable definition
> order was not a big enough deal to do in this patch, either, so...).
I will send out a series, that is based on this patch shortly;
as I fuzzed again with the small variables, that series doesn't
apply on this version but the version to be sent out shortly.
>
> Queued. Thanks.
please replace with the following series.
^ permalink raw reply
* [PATCH 5/5] unpack-trees: factor working tree update out of check_updates
From: Stefan Beller @ 2017-01-06 21:03 UTC (permalink / raw)
To: gitster, l.s.r; +Cc: git, Stefan Beller
In-Reply-To: <20170106210330.31761-1-sbeller@google.com>
This makes check_updates shorter and easier to understand.
Signed-off-by: Stefan Beller <sbeller@google.com>
---
unpack-trees.c | 40 ++++++++++++++++++++++++++--------------
1 file changed, 26 insertions(+), 14 deletions(-)
diff --git a/unpack-trees.c b/unpack-trees.c
index b954ec1233..b40c069b1b 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -275,67 +275,79 @@ static struct progress *get_progress(struct unpack_trees_options *o)
struct index_state *index = &o->result;
if (!o->update || !o->verbose_update)
return NULL;
for (; cnt < index->cache_nr; cnt++) {
const struct cache_entry *ce = index->cache[cnt];
if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
total++;
}
return start_progress_delay(_("Checking out files"),
total, 50, 1);
}
-static int check_updates(struct unpack_trees_options *o)
+static int update_working_tree_files(struct unpack_trees_options *o,
+ struct progress *progress,
+ unsigned start_cnt)
{
- unsigned cnt = 0;
+ unsigned cnt = start_cnt;
int i, errs = 0;
- struct progress *progress = NULL;
struct index_state *index = &o->result;
struct checkout state = CHECKOUT_INIT;
state.force = 1;
state.quiet = 1;
state.refresh_cache = 1;
state.istate = index;
- progress = get_progress(o);
-
- if (o->update)
- git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
-
- cnt = remove_workingtree_files(o, progress);
- remove_marked_cache_entries(index);
- remove_scheduled_dirs();
-
for (i = 0; i < index->cache_nr; i++) {
struct cache_entry *ce = index->cache[i];
if (ce->ce_flags & CE_UPDATE) {
if (ce->ce_flags & CE_WT_REMOVE)
die("BUG: both update and delete flags are set on %s",
ce->name);
display_progress(progress, ++cnt);
ce->ce_flags &= ~CE_UPDATE;
- if (o->update && !o->dry_run) {
+ if (o->update && !o->dry_run)
errs |= checkout_entry(ce, &state, NULL);
- }
}
}
+
+ return errs;
+}
+
+static int check_updates(struct unpack_trees_options *o)
+{
+ struct progress *progress = NULL;
+ struct index_state *index = &o->result;
+ int errs;
+ unsigned total_removed;
+
+ progress = get_progress(o);
+
+ if (o->update)
+ git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
+
+ total_removed = remove_workingtree_files(o, progress);
+ remove_marked_cache_entries(index);
+ remove_scheduled_dirs();
+ errs = update_working_tree_files(o, progress, total_removed);
+
stop_progress(&progress);
if (o->update)
git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
return errs != 0;
}
static int verify_uptodate_sparse(const struct cache_entry *ce,
struct unpack_trees_options *o);
static int verify_absent_sparse(const struct cache_entry *ce,
enum unpack_trees_error_types,
struct unpack_trees_options *o);
static int apply_sparse_checkout(struct index_state *istate,
struct cache_entry *ce,
struct unpack_trees_options *o)
--
2.11.0.31.g919a8d0.dirty
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox