From: Robin Rosenberg <robin.rosenberg@dewire.com>
To: spearce@spearce.org
Cc: git@vger.kernel.org, Robin Rosenberg <robin.rosenberg@dewire.com>
Subject: [EGIT PATCH 1/3] Add ref rename support to JGit
Date: Thu, 7 May 2009 01:32:59 +0200 [thread overview]
Message-ID: <1241652781-16873-2-git-send-email-robin.rosenberg@dewire.com> (raw)
In-Reply-To: <1241652781-16873-1-git-send-email-robin.rosenberg@dewire.com>
Now refs can be renamed. The intent is that should be safe. Only the named
refs and associated logs are updated. Any symbolic refs referring to the renames
branches are unaffected, except HEAD.
Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---
.../tst/org/spearce/jgit/lib/RefUpdateTest.java | 109 +++++++++++++++++++
.../src/org/spearce/jgit/lib/RefDatabase.java | 32 +++++-
.../src/org/spearce/jgit/lib/RefLogWriter.java | 21 ++++-
.../src/org/spearce/jgit/lib/RefUpdate.java | 112 +++++++++++++++++++-
.../src/org/spearce/jgit/lib/Repository.java | 33 ++++++
5 files changed, 303 insertions(+), 4 deletions(-)
diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefUpdateTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefUpdateTest.java
index 55d7441..b02773d 100644
--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefUpdateTest.java
+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefUpdateTest.java
@@ -42,6 +42,7 @@
import java.util.Map;
import java.util.Map.Entry;
+import org.spearce.jgit.lib.RefUpdate.RenameRefUpdates;
import org.spearce.jgit.lib.RefUpdate.Result;
import org.spearce.jgit.revwalk.RevCommit;
@@ -155,4 +156,112 @@ public void testRefKeySameAsOrigName() {
}
}
+
+ public void testRenameBranchNoPreviousLog() throws IOException {
+ assertFalse(new File(db.getDirectory(),"logs/refs/heads/b").exists()); // no log on old branch
+ ObjectId rb = db.resolve("refs/heads/b");
+ ObjectId oldHead = db.resolve(Constants.HEAD);
+ assertFalse(rb.equals(oldHead)); // assumption for this test
+ RenameRefUpdates renameRef = db.renameRef("refs/heads/b",
+ "refs/heads/new/name");
+ Result result = renameRef.rename();
+ assertEquals(Result.RENAMED, result);
+ assertEquals(rb, db.resolve("refs/heads/new/name"));
+ assertNull(db.resolve("refs/heads/b"));
+ assertTrue(new File(db.getDirectory(),"logs/refs/heads/new/name").exists());
+ assertFalse(new File(db.getDirectory(),"logs/refs/heads/b").exists());
+ assertEquals(oldHead, db.resolve(Constants.HEAD));
+ // TODO: test coprivate void assertNotEquals(ObjectId rb, ObjectId oldHead) {
+ // TODO Auto-generated method stub
+ }
+
+ public void testRenameBranchHasPreviousLog() throws IOException {
+ ObjectId rb = db.resolve("refs/heads/b");
+ ObjectId oldHead = db.resolve(Constants.HEAD);
+ assertFalse(rb.equals(oldHead)); // assumption for this test
+ RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
+ assertTrue(new File(db.getDirectory(),"logs/refs/heads/b").exists()); // no log on old branch
+ RenameRefUpdates renameRef = db.renameRef("refs/heads/b",
+ "refs/heads/new/name");
+ Result result = renameRef.rename();
+ assertEquals(Result.RENAMED, result);
+ assertEquals(rb, db.resolve("refs/heads/new/name"));
+ assertNull(db.resolve("refs/heads/b"));
+ assertTrue(new File(db.getDirectory(),"logs/refs/heads/new/name").exists());
+ assertFalse(new File(db.getDirectory(),"logs/refs/heads/b").exists());
+ assertEquals(oldHead, db.resolve(Constants.HEAD));
+ // TODO: test content of log file
+ }
+
+ public void testRenameCurrentBranch() throws IOException {
+ ObjectId rb = db.resolve("refs/heads/b");
+ db.link(Constants.HEAD, "refs/heads/b");
+ ObjectId oldHead = db.resolve(Constants.HEAD);
+ assertTrue(rb.equals(oldHead)); // assumption for this test
+ RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
+ assertTrue(new File(db.getDirectory(),"logs/refs/heads/b").exists()); // no log on old branch
+ RenameRefUpdates renameRef = db.renameRef("refs/heads/b",
+ "refs/heads/new/name");
+ Result result = renameRef.rename();
+ assertEquals(Result.RENAMED, result);
+ assertEquals(rb, db.resolve("refs/heads/new/name"));
+ assertNull(db.resolve("refs/heads/b"));
+ assertTrue(new File(db.getDirectory(),"logs/refs/heads/new/name").exists());
+ assertFalse(new File(db.getDirectory(),"logs/refs/heads/b").exists());
+ assertEquals(rb, db.resolve(Constants.HEAD));
+ // TODO: test content of log file
+ }
+
+ public void testRenameBranchCannotLockFirstBranch() throws IOException {
+ // "someone" has branch b locked
+ assertTrue(new LockFile(new File(db.getDirectory(), "refs/heads/b")).lock());
+
+ // setup
+ ObjectId rb = db.resolve("refs/heads/b");
+ db.link(Constants.HEAD, "refs/heads/b");
+ ObjectId oldHead = db.resolve(Constants.HEAD);
+ assertTrue(rb.equals(oldHead)); // assumption for this test
+ RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
+ assertTrue(new File(db.getDirectory(),"logs/refs/heads/b").exists()); // no log on old branch
+
+ // Now this is our test
+ RenameRefUpdates renameRef = db.renameRef("refs/heads/b",
+ "refs/heads/new/name");
+ Result result = renameRef.rename();
+ assertEquals(Result.LOCK_FAILURE, result);
+
+ // Check that the involved refs are sane despite the failure
+ assertFalse(new File(db.getDirectory(), "refs/heads/new/name").exists());
+ assertFalse(new File(db.getDirectory(), "refs/heads/new/name.lock").exists());
+ assertFalse(new File(db.getDirectory(), "logs/refs/heads/b/lock").exists());
+ assertFalse(new File(db.getDirectory(), "logs/refs/heads/new/name.lock").exists());
+ assertEquals(rb, db.resolve(Constants.HEAD));
+ }
+
+ public void testRenameBranchCannotLockHEAD() throws IOException {
+ // setup
+ ObjectId rb = db.resolve("refs/heads/b");
+ db.link(Constants.HEAD, "refs/heads/b");
+
+ // "someone" has branch b locked
+ assertTrue(new LockFile(new File(db.getDirectory(), "HEAD")).lock());
+
+ ObjectId oldHead = db.resolve(Constants.HEAD);
+ assertTrue(rb.equals(oldHead)); // assumption for this test
+ RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b");
+ assertTrue(new File(db.getDirectory(),"logs/refs/heads/b").exists()); // no log on old branch
+
+ // Now this is our test
+ RenameRefUpdates renameRef = db.renameRef("refs/heads/b",
+ "refs/heads/new/name");
+ Result result = renameRef.rename();
+ assertEquals(Result.LOCK_FAILURE, result);
+
+ // Check that the involved refs are sane despite the failure
+ assertFalse(new File(db.getDirectory(), "refs/heads/new/name").exists());
+ assertFalse(new File(db.getDirectory(), "refs/heads/new/name.lock").exists());
+ assertFalse(new File(db.getDirectory(), "logs/refs/heads/b/lock").exists());
+ assertFalse(new File(db.getDirectory(), "logs/refs/heads/new/name.lock").exists());
+ assertEquals(rb, db.resolve(Constants.HEAD));
+ }
}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java
index 87f26bf..a73467a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java
@@ -52,6 +52,7 @@
import org.spearce.jgit.errors.ObjectWritingException;
import org.spearce.jgit.lib.Ref.Storage;
+import org.spearce.jgit.lib.RefUpdate.RenameRefUpdates;
import org.spearce.jgit.util.FS;
import org.spearce.jgit.util.NB;
@@ -148,6 +149,25 @@ synchronized (this) {
}
/**
+ * An set of update operations for renaming a ref
+ *
+ * @param fromRef Old ref name
+ * @param toRef New ref name
+ * @return a RefUpdate operation to rename a ref
+ * @throws IOException
+ */
+ public RenameRefUpdates newRename(String fromRef, String toRef) throws IOException {
+ refreshPackedRefs();
+ Ref f = readRefBasic(fromRef, 0);
+ Ref t = readRefBasic(toRef, 0);
+ if (t != null)
+ throw new IOException("Ref rename target exists: " + t.getName());
+ RefUpdate refUpdateFrom = new RefUpdate(this, f, fileForRef(f.getName()));
+ RefUpdate refUpdateTo = db.updateRef(toRef);
+ return new RenameRefUpdates(refUpdateTo, refUpdateFrom, null);
+ }
+
+ /**
* Writes a symref (e.g. HEAD) to disk
*
* @param name
@@ -160,11 +180,19 @@ void link(final String name, final String target) throws IOException {
final byte[] content = Constants.encode("ref: " + target + "\n");
lockAndWriteFile(fileForRef(name), content);
synchronized (this) {
+ looseSymRefs.remove(name);
setModified();
}
db.fireRefsMaybeChanged();
}
+ void uncacheSymRef(String name) {
+ synchronized(this) {
+ looseSymRefs.remove(name);
+ setModified();
+ }
+ }
+
private void setModified() {
lastRefModification = refModificationCounter++;
}
@@ -484,8 +512,8 @@ private void lockAndWriteFile(File file, byte[] content) throws IOException {
}
synchronized void removePackedRef(String name) throws IOException {
- packedRefs.remove(name);
- writePackedRefs();
+ if (packedRefs.remove(name) != null)
+ writePackedRefs();
}
private void writePackedRefs() throws IOException {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java
index a077051..bbf26eb 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java
@@ -44,7 +44,7 @@
import java.io.IOException;
/**
- * Utility class to add reflog entries
+ * Utility class to work with reflog files
*
* @author Dave Watson
*/
@@ -58,6 +58,25 @@ static void append(final RefUpdate u, final String msg) throws IOException {
appendOneRecord(oldId, newId, ident, msg, db, u.getName());
}
+ static boolean renameTo(final Repository db, final RefUpdate from,
+ final RefUpdate to) throws IOException {
+ final File logdir = new File(db.getDirectory(), Constants.LOGS);
+ final File reflogFrom = new File(logdir, from.getName());
+ if (!reflogFrom.exists())
+ return true;
+ final File reflogTo = new File(logdir, to.getName());
+ final File refdirTo = reflogTo.getParentFile();
+ if (!refdirTo.exists() && !refdirTo.mkdirs()) {
+ throw new IOException("Cannot create directory " + refdirTo);
+ }
+ if (!reflogFrom.renameTo(reflogTo)) {
+ reflogTo.delete(); // try
+ throw new IOException("Cannot rename " + reflogFrom + " to "
+ + reflogTo);
+ }
+ return true;
+ }
+
private static void appendOneRecord(final ObjectId oldId,
final ObjectId newId, PersonIdent ident, final String msg,
final Repository db, final String refName) throws IOException {
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
index a9ab73b..8ecccfe 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java
@@ -50,6 +50,100 @@
* Updates any locally stored ref.
*/
public class RefUpdate {
+ /**
+ * A RefUpdate combination for renaming a ref
+ */
+ public static class RenameRefUpdates {
+ RenameRefUpdates(final RefUpdate toUpdate, final RefUpdate fromUpdate,
+ final RefUpdate headUpdate) {
+ newToUpdate = toUpdate;
+ oldFromDelete = fromUpdate;
+ }
+ private RefUpdate newToUpdate;
+
+ private RefUpdate oldFromDelete;
+
+ private Result renameResult;
+
+ /**
+ * @return result of rename operation
+ */
+ public Result getResult() {
+ return renameResult;
+ }
+
+ /**
+ * @return the result of the new ref update
+ * @throws IOException
+ */
+ public Result rename() throws IOException {
+ LockFile lockFileFrom = new LockFile(oldFromDelete.looseFile);
+ boolean lockFrom = lockFileFrom.lock();
+ if (!lockFrom)
+ return Result.LOCK_FAILURE;
+ LockFile lockFileTo = null;
+ try {
+ lockFileTo = new LockFile(newToUpdate.looseFile);
+ boolean lockTo = lockFileTo.lock();
+ if (!lockTo) {
+ lockFileFrom.unlock();
+ return renameResult = Result.LOCK_FAILURE;
+ }
+ } catch (IOException e) {
+ lockFileFrom.unlock();
+ throw e;
+ }
+ LockFile lockFileHEAD = new LockFile(new File(oldFromDelete.db.getRepository().getDirectory(), Constants.HEAD));
+ boolean renameHEADtoo;
+ try {
+ boolean lockHEAD = lockFileHEAD.lock();
+ renameHEADtoo = oldFromDelete.db.readRef(Constants.HEAD).getName().equals(oldFromDelete.getName());
+ if (!renameHEADtoo)
+ lockFileHEAD.unlock();
+ else {
+ if (!lockHEAD) {
+ lockFileFrom.unlock();
+ lockFileTo.unlock();
+ return renameResult = Result.LOCK_FAILURE;
+ }
+ }
+ } catch (IOException e) {
+ lockFileHEAD.unlock();
+ lockFileFrom.unlock();
+ lockFileTo.unlock();
+ throw e;
+ }
+ try {
+ UpdateStore toStore = newToUpdate.newUpdateStore();
+ if (RefLogWriter.renameTo(oldFromDelete.getRepository(), oldFromDelete, newToUpdate)) {
+ newToUpdate.setNewObjectId(oldFromDelete.getOldObjectId());
+ newToUpdate.setExpectedOldObjectId(oldFromDelete.getOldObjectId());
+ newToUpdate.setRefLogMessage("jgit branch: renamed " + oldFromDelete.getName() + " to " + oldFromDelete.getName(), false);
+ newToUpdate.result = toStore.store(lockFileTo, Result.RENAMED);
+ DeleteStore fromStore = oldFromDelete.newDeleteStore();
+ Result store = fromStore.store(lockFileFrom, Result.RENAMED);
+ if (renameHEADtoo) {
+ final byte[] content = Constants.encode("ref: " + newToUpdate.getName() + "\n");
+ lockFileHEAD.write(content);
+ synchronized (this) {
+ oldFromDelete.db.getRepository().uncacheSymRef(Constants.HEAD);
+ }
+ if (!lockFileHEAD.commit())
+ throw new IOException("Failed to commit HEAD during rename");
+ }
+ oldFromDelete.db.getRepository().fireRefsMaybeChanged();
+ return store;
+ } else {
+ return Result.IO_FAILURE;
+ }
+ } finally {
+ lockFileFrom.unlock();
+ lockFileTo.unlock();
+ lockFileHEAD.unlock();
+ }
+ }
+ }
+
/** Status of an update request. */
public static enum Result {
/** The ref update/delete has not been attempted by the caller. */
@@ -125,7 +219,13 @@
* This kind of error doesn't include {@link #LOCK_FAILURE}, which is a
* different case.
*/
- IO_FAILURE
+ IO_FAILURE,
+
+ /**
+ * The ref was renamed from another name
+ * <p>
+ */
+ RENAMED
}
/** Repository the ref is stored in. */
@@ -478,6 +578,8 @@ else if (status == Result.FAST_FORWARD)
msg += ": fast forward";
else if (status == Result.NEW)
msg += ": created";
+ else if (status == Result.RENAMED)
+ msg += ": renamed";
}
RefLogWriter.append(this, msg);
if (!lock.commit())
@@ -553,4 +655,12 @@ private static int count(final String s, final char c) {
}
return count;
}
+
+ private UpdateStore newUpdateStore() {
+ return new UpdateStore();
+ }
+
+ private DeleteStore newDeleteStore() {
+ return new DeleteStore();
+ }
}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
index 5baa7a0..e704aeb 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
@@ -468,6 +468,22 @@ public RefUpdate updateRef(final String ref) throws IOException {
}
/**
+ * Create a command to rename a ref in this repository
+ *
+ * @param fromRef
+ * name of ref to rename from
+ * @param toRef
+ * name of ref to rename to
+ * @return an update command that knows how to rename a branch to another.
+ * @throws IOException
+ * the rename could not be performed.
+ *
+ */
+ public RefUpdate.RenameRefUpdates renameRef(final String fromRef, final String toRef) throws IOException {
+ return refs.newRename(fromRef, toRef);
+ }
+
+ /**
* Parse a git revision string and return an object id.
*
* Currently supported is combinations of these.
@@ -1067,4 +1083,21 @@ public void scanForRepoChanges() throws IOException {
getAllRefs(); // This will look for changes to refs
getIndex(); // This will detect changes in the index
}
+
+ /**
+ * Writes a symref (e.g. HEAD) to disk
+ *
+ * @param name
+ * symref name
+ * @param target
+ * pointed to ref
+ * @throws IOException
+ */
+ public void link(final String name, final String target) throws IOException {
+ refs.link(name, target);
+ }
+
+ void uncacheSymRef(String name) {
+ refs.uncacheSymRef(name);
+ }
}
--
1.6.3.rc2.1.g868b6
next prev parent reply other threads:[~2009-05-06 23:33 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-05-06 23:32 [EGIT PATCH 0/3] Rename refs Robin Rosenberg
2009-05-06 23:32 ` Robin Rosenberg [this message]
2009-05-06 23:33 ` [EGIT PATCH 2/3] Use Constants.R_* in Branch dialog Robin Rosenberg
2009-05-06 23:33 ` [EGIT PATCH 3/3] Add ref rename support to the branch dialog Robin Rosenberg
2009-05-07 15:51 ` [EGIT PATCH 1/3] Add ref rename support to JGit Shawn O. Pearce
2009-05-19 23:13 ` [EGIT PATCH 0/6] Ref rename Robin Rosenberg
2009-05-19 23:13 ` [EGIT PATCH 1/6] Make sure we get the right storage for loose/pack/loose and packed refs Robin Rosenberg
2009-05-19 23:13 ` [EGIT PATCH 2/6] Add ref rename support to JGit Robin Rosenberg
2009-05-19 23:13 ` [EGIT PATCH 3/6] Add ref rename support to the branch dialog Robin Rosenberg
2009-05-19 23:13 ` [EGIT PATCH 4/6] Allow non-ASCII ref names when writing the packed-refs file Robin Rosenberg
2009-05-19 23:13 ` [EGIT PATCH 5/6] Use Constants.PACKED_REFS in RefWriter Robin Rosenberg
2009-05-19 23:13 ` [EGIT PATCH 6/6] Improve error reporting in the branch dialog Robin Rosenberg
2009-05-20 22:16 ` [EGIT PATCH 2/6] Add ref rename support to JGit Shawn O. Pearce
2009-05-27 22:08 ` [EGIT PATCH 00/10] Rename support Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 01/10] Make sure we get the right storage for loose/pack/loose and packed refs Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 02/10] Add a toString for debugging to RemoteRefUpdate Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 03/10] Add a toString to LockFile Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 04/10] Add more tests for RefUpdate Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 05/10] Do not write to the reflog unless the refupdate logmessage is set Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 06/10] Add a utility method for shortening long ref names to short ones Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 07/10] Set a nice reflog message in the branch command Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 08/10] Add ref rename support to JGit Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 09/10] Add ref rename support to the branch dialog Robin Rosenberg
2009-05-27 22:08 ` [EGIT PATCH 10/10] Improve error reporting in " Robin Rosenberg
2009-06-03 16:43 ` [EGIT PATCH 08/10] Add ref rename support to JGit Shawn O. Pearce
2009-06-03 15:41 ` [EGIT PATCH 05/10] Do not write to the reflog unless the refupdate logmessage is set Shawn O. Pearce
2009-06-07 22:27 ` Robin Rosenberg
2009-06-07 22:44 ` Shawn O. Pearce
2009-05-20 21:43 ` [EGIT PATCH 1/6] Make sure we get the right storage for loose/pack/loose and packed refs Shawn O. Pearce
2009-05-21 15:22 ` Robin Rosenberg
2009-05-21 15:48 ` Shawn O. Pearce
2009-06-10 21:22 ` [EGIT PATCH v5 0/7] Ref rename support again Robin Rosenberg
2009-06-10 21:22 ` [EGIT PATCH v5 1/7] Add methods to RawParseUtils for scanning backwards Robin Rosenberg
2009-06-10 21:22 ` [EGIT PATCH v5 2/7] Add a ref log reader class Robin Rosenberg
2009-06-10 21:22 ` [EGIT PATCH v5 3/7] Do not write to the reflog unless the refupdate logmessage is set Robin Rosenberg
2009-06-10 21:22 ` [EGIT PATCH v5 4/7] Add ref rename support to JGit Robin Rosenberg
2009-06-10 21:22 ` [EGIT PATCH v5 5/7] Add ref rename support to the branch dialog Robin Rosenberg
2009-06-10 21:22 ` [EGIT PATCH v5 6/7] Improve error reporting in " Robin Rosenberg
2009-06-10 21:22 ` [EGIT PATCH v5 7/7] Remove a TAB from the message Egit generates into the reflog on commit Robin Rosenberg
2009-06-12 20:02 ` [EGIT PATCH v5 5/7] Add ref rename support to the branch dialog Shawn O. Pearce
2009-06-14 19:47 ` [EGIT PATCH v6 5a/7] Warn for unlocalized string literals in ui plugin Robin Rosenberg
2009-06-14 19:47 ` [EGIT PATCH v6 5b/6] Add ref rename support to the branch dialog Robin Rosenberg
2009-06-14 19:47 ` [EGIT PATCH v6 6/7] Improve error reporting in " Robin Rosenberg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1241652781-16873-2-git-send-email-robin.rosenberg@dewire.com \
--to=robin.rosenberg@dewire.com \
--cc=git@vger.kernel.org \
--cc=spearce@spearce.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).