Skip to content

Commit 1e5d454

Browse files
committed
Merge branch 'ld/p4-unshelve'
"git p4 unshelve" improvements. * ld/p4-unshelve: git-p4: fully support unshelving changelists git-p4: unshelve into refs/remotes/p4-unshelved, not refs/remotes/p4/unshelved git-p4: do not fail in verbose mode for missing 'fileSize' key
2 parents eff5d69 + 89143ac commit 1e5d454

File tree

3 files changed

+117
-58
lines changed

3 files changed

+117
-58
lines changed

Documentation/git-p4.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,21 +174,21 @@ $ git p4 submit --update-shelve 1234 --update-shelve 2345
174174
Unshelve
175175
~~~~~~~~
176176
Unshelving will take a shelved P4 changelist, and produce the equivalent git commit
177-
in the branch refs/remotes/p4/unshelved/<changelist>.
177+
in the branch refs/remotes/p4-unshelved/<changelist>.
178178

179179
The git commit is created relative to the current origin revision (HEAD by default).
180-
If the shelved changelist's parent revisions differ, git-p4 will refuse to unshelve;
181-
you need to be unshelving onto an equivalent tree.
180+
A parent commit is created based on the origin, and then the unshelve commit is
181+
created based on that.
182182

183183
The origin revision can be changed with the "--origin" option.
184184

185-
If the target branch in refs/remotes/p4/unshelved already exists, the old one will
185+
If the target branch in refs/remotes/p4-unshelved already exists, the old one will
186186
be renamed.
187187

188188
----
189189
$ git p4 sync
190190
$ git p4 unshelve 12345
191-
$ git show refs/remotes/p4/unshelved/12345
191+
$ git show p4-unshelved/12345
192192
<submit more changes via p4 to the same files>
193193
$ git p4 unshelve 12345
194194
<refuses to unshelve until git is in sync with p4 again>

git-p4.py

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,9 @@ def processContent(self, git_mode, relPath, contents):
13061306
return LargeFileSystem.processContent(self, git_mode, relPath, contents)
13071307

13081308
class Command:
1309+
delete_actions = ( "delete", "move/delete", "purge" )
1310+
add_actions = ( "add", "move/add" )
1311+
13091312
def __init__(self):
13101313
self.usage = "usage: %prog [options]"
13111314
self.needsGit = True
@@ -2524,7 +2527,6 @@ def map_in_client(self, depot_path):
25242527
return ""
25252528

25262529
class P4Sync(Command, P4UserMap):
2527-
delete_actions = ( "delete", "move/delete", "purge" )
25282530

25292531
def __init__(self):
25302532
Command.__init__(self)
@@ -2612,20 +2614,7 @@ def checkpoint(self):
26122614
if self.verbose:
26132615
print("checkpoint finished: " + out)
26142616

2615-
def cmp_shelved(self, path, filerev, revision):
2616-
""" Determine if a path at revision #filerev is the same as the file
2617-
at revision @revision for a shelved changelist. If they don't match,
2618-
unshelving won't be safe (we will get other changes mixed in).
2619-
2620-
This is comparing the revision that the shelved changelist is *based* on, not
2621-
the shelved changelist itself.
2622-
"""
2623-
ret = p4Cmd(["diff2", "{0}#{1}".format(path, filerev), "{0}@{1}".format(path, revision)])
2624-
if verbose:
2625-
print("p4 diff2 path %s filerev %s revision %s => %s" % (path, filerev, revision, ret))
2626-
return ret["status"] == "identical"
2627-
2628-
def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0, origin_revision = 0):
2617+
def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0):
26292618
self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
26302619
for path in self.cloneExclude]
26312620
files = []
@@ -2650,17 +2639,6 @@ def extractFilesFromCommit(self, commit, shelved=False, shelved_cl = 0, origin_r
26502639
file["type"] = commit["type%s" % fnum]
26512640
if shelved:
26522641
file["shelved_cl"] = int(shelved_cl)
2653-
2654-
# For shelved changelists, check that the revision of each file that the
2655-
# shelve was based on matches the revision that we are using for the
2656-
# starting point for git-fast-import (self.initialParent). Otherwise
2657-
# the resulting diff will contain deltas from multiple commits.
2658-
2659-
if file["action"] != "add" and \
2660-
not self.cmp_shelved(path, file["rev"], origin_revision):
2661-
sys.exit("change {0} not based on {1} for {2}, cannot unshelve".format(
2662-
commit["change"], self.initialParent, path))
2663-
26642642
files.append(file)
26652643
fnum = fnum + 1
26662644
return files
@@ -2775,7 +2753,10 @@ def streamOneP4File(self, file, contents):
27752753
relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
27762754
relPath = self.encodeWithUTF8(relPath)
27772755
if verbose:
2778-
size = int(self.stream_file['fileSize'])
2756+
if 'fileSize' in self.stream_file:
2757+
size = int(self.stream_file['fileSize'])
2758+
else:
2759+
size = 0 # deleted files don't get a fileSize apparently
27792760
sys.stdout.write('\r%s --> %s (%i MB)\n' % (file['depotFile'], relPath, size/1024/1024))
27802761
sys.stdout.flush()
27812762

@@ -3029,7 +3010,7 @@ def hasBranchPrefix(self, path):
30293010
print('Ignoring file outside of prefix: {0}'.format(path))
30303011
return hasPrefix
30313012

3032-
def commit(self, details, files, branch, parent = ""):
3013+
def commit(self, details, files, branch, parent = "", allow_empty=False):
30333014
epoch = details["time"]
30343015
author = details["user"]
30353016
jobs = self.extractJobsFromCommit(details)
@@ -3043,7 +3024,10 @@ def commit(self, details, files, branch, parent = ""):
30433024
files = [f for f in files
30443025
if self.inClientSpec(f['path']) and self.hasBranchPrefix(f['path'])]
30453026

3046-
if not files and not gitConfigBool('git-p4.keepEmptyCommits'):
3027+
if gitConfigBool('git-p4.keepEmptyCommits'):
3028+
allow_empty = True
3029+
3030+
if not files and not allow_empty:
30473031
print('Ignoring revision {0} as it would produce an empty commit.'
30483032
.format(details['change']))
30493033
return
@@ -3384,10 +3368,10 @@ def searchParent(self, parent, branch, target):
33843368
else:
33853369
return None
33863370

3387-
def importChanges(self, changes, shelved=False, origin_revision=0):
3371+
def importChanges(self, changes, origin_revision=0):
33883372
cnt = 1
33893373
for change in changes:
3390-
description = p4_describe(change, shelved)
3374+
description = p4_describe(change)
33913375
self.updateOptionDict(description)
33923376

33933377
if not self.silent:
@@ -3457,7 +3441,7 @@ def importChanges(self, changes, shelved=False, origin_revision=0):
34573441
print("Parent of %s not found. Committing into head of %s" % (branch, parent))
34583442
self.commit(description, filesForCommit, branch, parent)
34593443
else:
3460-
files = self.extractFilesFromCommit(description, shelved, change, origin_revision)
3444+
files = self.extractFilesFromCommit(description)
34613445
self.commit(description, files, self.branch,
34623446
self.initialParent)
34633447
# only needed once, to connect to the previous commit
@@ -3953,7 +3937,7 @@ def __init__(self):
39533937
]
39543938
self.verbose = False
39553939
self.noCommit = False
3956-
self.destbranch = "refs/remotes/p4/unshelved"
3940+
self.destbranch = "refs/remotes/p4-unshelved"
39573941

39583942
def renameBranch(self, branch_name):
39593943
""" Rename the existing branch to branch_name.N
@@ -3985,6 +3969,32 @@ def findLastP4Revision(self, starting_point):
39853969

39863970
sys.exit("could not find git-p4 commits in {0}".format(self.origin))
39873971

3972+
def createShelveParent(self, change, branch_name, sync, origin):
3973+
""" Create a commit matching the parent of the shelved changelist 'change'
3974+
"""
3975+
parent_description = p4_describe(change, shelved=True)
3976+
parent_description['desc'] = 'parent for shelved changelist {}\n'.format(change)
3977+
files = sync.extractFilesFromCommit(parent_description, shelved=False, shelved_cl=change)
3978+
3979+
parent_files = []
3980+
for f in files:
3981+
# if it was added in the shelved changelist, it won't exist in the parent
3982+
if f['action'] in self.add_actions:
3983+
continue
3984+
3985+
# if it was deleted in the shelved changelist it must not be deleted
3986+
# in the parent - we might even need to create it if the origin branch
3987+
# does not have it
3988+
if f['action'] in self.delete_actions:
3989+
f['action'] = 'add'
3990+
3991+
parent_files.append(f)
3992+
3993+
sync.commit(parent_description, parent_files, branch_name,
3994+
parent=origin, allow_empty=True)
3995+
print("created parent commit for {0} based on {1} in {2}".format(
3996+
change, self.origin, branch_name))
3997+
39883998
def run(self, args):
39893999
if len(args) != 1:
39904000
return False
@@ -3994,9 +4004,8 @@ def run(self, args):
39944004

39954005
sync = P4Sync()
39964006
changes = args
3997-
sync.initialParent = self.origin
39984007

3999-
# use the first change in the list to construct the branch to unshelve into
4008+
# only one change at a time
40004009
change = changes[0]
40014010

40024011
# if the target branch already exists, rename it
@@ -4009,14 +4018,21 @@ def run(self, args):
40094018
sync.suppress_meta_comment = True
40104019

40114020
settings = self.findLastP4Revision(self.origin)
4012-
origin_revision = settings['change']
40134021
sync.depotPaths = settings['depot-paths']
40144022
sync.branchPrefixes = sync.depotPaths
40154023

40164024
sync.openStreams()
40174025
sync.loadUserMapFromCache()
40184026
sync.silent = True
4019-
sync.importChanges(changes, shelved=True, origin_revision=origin_revision)
4027+
4028+
# create a commit for the parent of the shelved changelist
4029+
self.createShelveParent(change, branch_name, sync, self.origin)
4030+
4031+
# create the commit for the shelved changelist itself
4032+
description = p4_describe(change, True)
4033+
files = sync.extractFilesFromCommit(description, True, change)
4034+
4035+
sync.commit(description, files, branch_name, "")
40204036
sync.closeStreams()
40214037

40224038
print("unshelved changelist {0} into {1}".format(change, branch_name))

t/t9832-unshelve.sh

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ test_expect_success 'init depot' '
1919
p4 add file1 &&
2020
p4 submit -d "change 1" &&
2121
: >file_to_delete &&
22+
: >file_to_move &&
2223
p4 add file_to_delete &&
23-
p4 submit -d "file to delete"
24+
p4 add file_to_move &&
25+
p4 submit -d "add files to delete"
2426
)
2527
'
2628

@@ -36,6 +38,8 @@ test_expect_success 'create shelved changelist' '
3638
echo "new file" >file2 &&
3739
p4 add file2 &&
3840
p4 delete file_to_delete &&
41+
p4 edit file_to_move &&
42+
p4 move file_to_move moved_file &&
3943
p4 opened &&
4044
p4 shelve -i <<EOF
4145
Change: new
@@ -47,19 +51,23 @@ Files:
4751
//depot/file1
4852
//depot/file2
4953
//depot/file_to_delete
54+
//depot/file_to_move
55+
//depot/moved_file
5056
EOF
5157
5258
) &&
5359
(
5460
cd "$git" &&
5561
change=$(last_shelved_change) &&
5662
git p4 unshelve $change &&
57-
git show refs/remotes/p4/unshelved/$change | grep -q "Further description" &&
58-
git cherry-pick refs/remotes/p4/unshelved/$change &&
63+
git show refs/remotes/p4-unshelved/$change | grep -q "Further description" &&
64+
git cherry-pick refs/remotes/p4-unshelved/$change &&
5965
test_path_is_file file2 &&
6066
test_cmp file1 "$cli"/file1 &&
6167
test_cmp file2 "$cli"/file2 &&
62-
test_path_is_missing file_to_delete
68+
test_path_is_missing file_to_delete &&
69+
test_path_is_missing file_to_move &&
70+
test_path_is_file moved_file
6371
)
6472
'
6573

@@ -88,10 +96,22 @@ EOF
8896
cd "$git" &&
8997
change=$(last_shelved_change) &&
9098
git p4 unshelve $change &&
91-
git diff refs/remotes/p4/unshelved/$change.0 refs/remotes/p4/unshelved/$change | grep -q file3
99+
git diff refs/remotes/p4-unshelved/$change.0 refs/remotes/p4-unshelved/$change | grep -q file3
92100
)
93101
'
94102

103+
shelve_one_file () {
104+
description="Change to be unshelved" &&
105+
file="$1" &&
106+
p4 shelve -i <<EOF
107+
Change: new
108+
Description:
109+
$description
110+
Files:
111+
$file
112+
EOF
113+
}
114+
95115
# This is the tricky case where the shelved changelist base revision doesn't
96116
# match git-p4's idea of the base revision
97117
#
@@ -108,29 +128,52 @@ test_expect_success 'create shelved changelist based on p4 change ahead of p4/ma
108128
p4 submit -d "change:foo" &&
109129
p4 edit file1 &&
110130
echo "bar" >>file1 &&
111-
p4 shelve -i <<EOF &&
112-
Change: new
113-
Description:
114-
Change to be unshelved
115-
Files:
116-
//depot/file1
117-
EOF
131+
shelve_one_file //depot/file1 &&
118132
change=$(last_shelved_change) &&
119-
p4 describe -S $change | grep -q "Change to be unshelved"
133+
p4 describe -S $change >out.txt &&
134+
grep -q "Change to be unshelved" out.txt
120135
)
121136
'
122137

123-
# Now try to unshelve it. git-p4 should refuse to do so.
138+
# Now try to unshelve it.
124139
test_expect_success 'try to unshelve the change' '
125140
test_when_finished cleanup_git &&
126141
(
127142
change=$(last_shelved_change) &&
128143
cd "$git" &&
129-
test_must_fail git p4 unshelve $change 2>out.txt &&
130-
grep -q "cannot unshelve" out.txt
144+
git p4 unshelve $change >out.txt &&
145+
grep -q "unshelved changelist $change" out.txt
131146
)
132147
'
133148

149+
# Specify the origin. Create 2 unrelated files, and check that
150+
# we only get the one in HEAD~, not the one in HEAD.
151+
152+
test_expect_success 'unshelve specifying the origin' '
153+
(
154+
cd "$cli" &&
155+
: >unrelated_file0 &&
156+
p4 add unrelated_file0 &&
157+
p4 submit -d "unrelated" &&
158+
: >unrelated_file1 &&
159+
p4 add unrelated_file1 &&
160+
p4 submit -d "unrelated" &&
161+
: >file_to_shelve &&
162+
p4 add file_to_shelve &&
163+
shelve_one_file //depot/file_to_shelve
164+
) &&
165+
test_when_finished cleanup_git &&
166+
git p4 clone --dest="$git" //depot/@all &&
167+
(
168+
cd "$git" &&
169+
change=$(last_shelved_change) &&
170+
git p4 unshelve --origin HEAD~ $change &&
171+
git checkout refs/remotes/p4-unshelved/$change &&
172+
test_path_is_file unrelated_file0 &&
173+
test_path_is_missing unrelated_file1 &&
174+
test_path_is_file file_to_shelve
175+
)
176+
'
134177
test_expect_success 'kill p4d' '
135178
kill_p4d
136179
'

0 commit comments

Comments
 (0)